
简介这是一套开箱即用的短网址生成与防红一体化网站源码面向Web开发初学者及PHP全栈实践者解决社交媒体推广、营销链接分发中因平台封禁导致访问中断的实际问题。资源包含73个文件主体为21个PHP后端逻辑文件含zise.php、api.php、fanghong.sql等核心模块、20个CSS样式文件与12个JS交互脚本辅以字体、图标及图片资源整体压缩包仅1.1MB轻量易部署。已有1250人学习下载说明其在实战教学与二次开发场景中具备良好验证基础。读者可直接搭建完整站点获得带用户管理、网址增删改查、访问统计、防红策略配置加密/代理/混淆的后台系统同时深入理解短码映射算法、SQL注入/XSS防护机制及前后端协同逻辑是掌握Web安全与URL服务架构的典型入门级项目。1. 短网址生成网站源码不是简单跳转而是防红链路的完整闭环你有没有遇到过这样的场景刚发出去的推广链接5分钟内就被微信/QQ/钉钉标红、拦截、提示“该网页包含风险内容”换域名重试第二天又红甚至用大厂短链服务如t.cn、dwz.cn生成的链接在某些群聊或私信里照样被折叠、无法点击。这不是玄学是平台风控策略升级后对「跳转链路可信度」的深度校验——它不只看最终落地页更盯住中间跳转环节的域名信誉、HTTPS强度、响应头规范、重定向链长度、JS行为特征。所谓“防红源码”本质是一套可控、可审计、可快速迭代的短链基础设施从URL编码、数据库存储、HTTP 302跳转控制到Referer过滤、UA白名单、IP限频、HTTPS强制跳转、HSTS头注入、CSP策略配置再到日志埋点与红链预警联动。它适合需要高频分发外链的运营团队、私域裂变工具开发者、SaaS产品嵌入式分享模块搭建者以及对第三方短链服务稳定性存疑的技术负责人。这不是一个“拿来即用”的静态页面而是一个需部署在自有服务器、可定制风控逻辑、能对接内部用户体系的轻量级Web服务。2. 用 PHP MySQL 搭建最小可用短链服务从零跑通核心跳转链路短网址生成的核心逻辑极简用户提交长 URL → 系统生成唯一短码如abc123→ 存入数据库 → 用户访问https://yourdomain.com/abc123→ 后端查库 → 302 重定向至原始 URL。但“防红”的关键恰恰藏在“查库”和“重定向”这两个动作之间的控制权里。我们不用 Laravel 或 ThinkPHP 这类全栈框架选择原生 PHP MySQL 组合原因有三一是启动快、资源占用低适合中小流量二是所有跳转逻辑完全透明便于插入风控钩子三是便于后续对接 Nginx 的map模块做前置 UA/Referer 过滤。下面以 PHP 8.1 MySQL 8.0 为基准环境构建最小可运行版本。2.1 数据库设计与初始化短码必须唯一且带状态字段防红不是靠“藏”而是靠“控”。短码不能仅存id, long_url, short_code必须预留风控干预入口。我们建表时加入status0启用1禁用2灰度、created_at、updated_at、hit_count用于识别异常爬虫、referer_whitelistJSON 字段存允许跳转的来源域名列表等字段。这是后续做 Referer 白名单、灰度放行、自动封禁的基础。CREATE TABLE short_urls ( id bigint unsigned NOT NULL AUTO_INCREMENT, long_url text NOT NULL, short_code varchar(12) NOT NULL UNIQUE COMMENT 短码如 abc123, status tinyint NOT NULL DEFAULT 0 COMMENT 0启用1禁用2灰度, hit_count int unsigned NOT NULL DEFAULT 0, referer_whitelist json DEFAULT NULL COMMENT [example.com, wechat.com], created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_short_code_status (short_code,status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;提示short_code必须加UNIQUE约束避免重复生成导致跳转错乱idx_short_code_status复合索引能极大提升查询性能——因为每次跳转都先按short_code查再过滤status0。2.2 短码生成算法避开易被误判的字符支持自定义长度很多开源短链项目用 base620-9a-zA-Z但0Oo、1lI在微信字体下极易混淆且l小写L和I大写i常被风控系统标记为“可疑混淆字符”。我们改用精简版 base58剔除0Oo1lI/只保留123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz共58个字符。生成逻辑如下// utils.php function generateShortCode($length 6) { $chars 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz; $code ; for ($i 0; $i $length; $i) { $code . $chars[random_int(0, strlen($chars) - 1)]; } return $code; } // 防止生成已存在短码最多重试5次 function getUniqueShortCode($pdo, $length 6) { for ($i 0; $i 5; $i) { $code generateShortCode($length); $stmt $pdo-prepare(SELECT COUNT(*) FROM short_urls WHERE short_code ?); $stmt-execute([$code]); if ($stmt-fetchColumn() 0) { return $code; } } throw new Exception(Failed to generate unique short code after 5 attempts); }参数说明$length 6是平衡安全性和长度的常用值。6位 base58 短码理论容量为 58⁶ ≈ 360 亿远超中小项目需求若需更高抗爆破性可设为 7 位2000 亿但注意微信对短链长度无硬限制过长反而影响传播。random_int()是 PHP 7.0 安全随机函数不可用rand()替代。2.3 核心跳转控制器302 重定向前插入 Referer 白名单校验这是防红最关键的一步。微信/QQ 的风控会检查跳转请求的Referer头是否来自其自家域名如https://servicewechat.com/、https://im.qq.com/或空直接输入地址栏访问。若Referer是http://evil-site.com则大概率触发红链。我们在重定向前强制校验// redirect.php require_once config.php; require_once utils.php; $shortCode $_GET[code] ?? ; if (empty($shortCode) || !preg_match(/^[1-9A-HJ-NP-Za-km-z]{6,12}$/, $shortCode)) { http_response_code(400); exit(Invalid short code); } try { $pdo new PDO($dsn, $db_user, $db_pass, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC ]); // 1. 先查状态禁用/灰度状态直接返回404不暴露存在性 $stmt $pdo-prepare(SELECT long_url, status, referer_whitelist FROM short_urls WHERE short_code ? AND status 0); $stmt-execute([$shortCode]); $row $stmt-fetch(); if (!$row) { http_response_code(404); exit(Not found); } $longUrl $row[long_url]; $referer $_SERVER[HTTP_REFERER] ?? ; // 2. Referer 白名单校验空Referer允许模拟用户直接访问 if (!empty($referer)) { $whitelist json_decode($row[referer_whitelist], true) ?: []; $host parse_url($referer, PHP_URL_HOST); if ($host !in_array($host, $whitelist) !in_array(*, $whitelist)) { // 记录异常访问但不阻断避免被探测 error_log(Blocked referer: {$referer} for short code {$shortCode}); http_response_code(403); exit(Forbidden); } } // 3. 更新命中次数异步记录避免阻塞跳转 $pdo-prepare(UPDATE short_urls SET hit_count hit_count 1 WHERE short_code ?)-execute([$shortCode]); // 4. 强制 HTTPS 跳转防降级攻击 if (empty($_SERVER[HTTPS]) || $_SERVER[HTTPS] ! on) { $httpsUrl https:// . $_SERVER[HTTP_HOST] . $_SERVER[REQUEST_URI]; header(Location: {$httpsUrl}, true, 301); exit; } // 5. 最终302跳转设置严格安全头 header(Content-Type: text/html; charsetutf-8); header(X-Frame-Options: DENY); header(X-Content-Type-Options: nosniff); header(Referrer-Policy: no-referrer); header(Strict-Transport-Security: max-age31536000; includeSubDomains; preload); header(Location: {$longUrl}, true, 302); exit; } catch (PDOException $e) { error_log(DB Error: . $e-getMessage()); http_response_code(500); exit(Server error); }逻辑说明这段代码不是“生成短链”的入口而是“访问短链”的入口即https://yourdomain.com/redirect.php?codeabc123。它做了五件事① 校验短码格式合法性② 查询并确认状态为启用③ 校验 Referer 是否在白名单支持*通配④ 强制跳转 HTTPS⑤ 发送 302 响应并附带全套安全头。其中Referrer-Policy: no-referrer是关键——它让浏览器在跳转到目标页时不发送 Referer避免把你的短链域名暴露给下游站点降低被关联封禁风险。3. Nginx 层面加固用 map 指令实现 UA/Referer 前置过滤PHP 层校验是兜底但高并发下恶意请求如扫描器、爬虫应在 Web 服务器层就拦截避免打到 PHP-FPM 进程。Nginx 的map指令能基于请求头动态设置变量配合if和return实现毫秒级过滤。我们针对两类高危请求做前置拦截① 明确的爬虫 UA如python-requests,curl/7.② 非法 Referer如已知黑产域名、空 Referer 但非微信/QQ。3.1 构建 UA 黑名单 map精准识别自动化工具在nginx.conf的http块中添加# /etc/nginx/conf.d/short-url.conf map $http_user_agent $bad_ua { default 0; ~*python-requests 1; ~*curl/[0-9] 1; ~*wget 1; ~*Go-http-client 1; ~*Java/ 1; ~*Apache-HttpClient 1; ~*okhttp 1; ~*PostmanRuntime 1; }参数说明map是 Nginx 的映射指令$http_user_agent是请求头变量$bad_ua是新变量。~*表示忽略大小写的正则匹配。这里列出的是常见自动化工具 UA 特征而非真实浏览器Chrome/Firefox/Safari/WeChat 内置浏览器均未列入。default 0表示默认不拦截只有匹配才设为 1。3.2 构建 Referer 白名单 map只允许可信来源跳转同样在http块中添加map $http_referer $bad_referer { default 1; # 默认视为非法 ~*https?://[^/]\.wechat\.com 0; ~*https?://[^/]\.qq\.com 0; ~*https?://[^/]\.qpic\.cn 0; ~*https?://[^/]\.weixin\.qq\.com 0; ~*https?://[^/]\.servicewechat\.com 0; ~*https?://[^/]\.im\.qq\.com 0; ~*^$ 0; # 空Referer允许用户直接输入短链 }注意default 1是关键设计——我们采用“黑名单思维下的白名单实现”。即默认所有 Referer 都非法只显式放行微信、QQ 及其子域名servicewechat.com是微信小程序域名qpic.cn是微信图片 CDN 域名常出现在公众号图文内。~*^$匹配空字符串代表用户在浏览器地址栏直接输入短链这种场景必须放行。3.3 在 server 块中应用过滤规则return 444 立即断连在你的短链站点server块中加入server { listen 80; server_name yourdomain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name yourdomain.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; # 前置拦截UA 或 Referer 不合法立即断连444 是 Nginx 特有“关闭连接”状态码不发任何响应 if ($bad_ua 1) { return 444; } if ($bad_referer 1) { return 444; } # 正常请求代理到 PHP location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }逻辑说明return 444是 Nginx 最高效的拦截方式——它不构造 HTTP 响应包直接关闭 TCP 连接对服务器资源消耗趋近于零。相比 PHP 层http_response_code(403)它能在毫秒级阻断恶意扫描保护后端不被压垮。注意if在location块外使用是 Nginx 官方允许的用于请求头判断无需担心性能问题。4. 防红避坑指南5 条血泪经验每一条都踩过真实翻车现场做短链防红最怕的不是技术不会而是“以为对了其实错了”。以下是我在线上环境反复验证、被微信/QQ 封过三次后总结的 5 条核心避坑点现象、原因、解法全部对应真实日志和抓包数据。4.1 现象短链在微信内第一次点开正常第二次点开就红原因微信对同一短链的跳转行为有“行为指纹”追踪。若 PHP 跳转时未设置Cache-Control: no-cache, no-store, must-revalidate微信 WebView 会缓存 302 响应第二次请求直接读缓存绕过你的 PHP 校验逻辑导致 Referer 白名单失效。解决在redirect.php的 302 响应头中强制加入缓存控制header(Cache-Control: no-cache, no-store, must-revalidate); header(Pragma: no-cache); header(Expires: 0);4.2 现象用 HTTPS 短链跳转 HTTP 目标页链接立刻被标红原因微信/QQ 对混合内容Mixed Content极度敏感。当短链域名是 HTTPS但跳转目标是 HTTP 时风控系统判定“链路不安全”直接拦截。这不是浏览器报错是平台主动封禁。解决在生成短链时前端或后端强制校验long_url协议头。若为http://拒绝生成或自动补全为https://需确保目标站支持 HTTPS。PHP 中增加if (stripos($longUrl, http://) 0) { $longUrl https:// . substr($longUrl, 7); // 后续需验证该 HTTPS 地址是否可访问用 curl_head 检查 200 }4.3 现象短链在 QQ 群里能点但在微信私聊里点不开提示“网页包含风险”原因微信私聊的 Referer 是https://servicewechat.com/...而 QQ 群是https://im.qq.com/...。你在referer_whitelist里只写了servicewechat.com漏掉了im.qq.com导致 QQ 请求被 PHP 层403拦截但微信误判为“链接本身有问题”。解决白名单 JSON 必须同时包含两者[servicewechat.com, im.qq.com, qpic.cn]且 Nginx 的map规则也要同步更新否则前置拦截会先干掉 QQ 请求。4.4 现象短链生成后用 curl 测试 302 正常但微信里就是打不开原因curl 默认不带 Referer而微信 WebView 会带上。你的 PHP 校验逻辑若对空 Referer 返回 403而非放行就会导致 curl 成功、微信失败。解决检查redirect.php中 Referer 校验逻辑确保if (empty($referer)) { /* 放行 */ }分支存在且位置在白名单校验之前。4.5 现象短链域名刚备案但一周内仍被频繁误判为“仿冒网站”原因新域名在微信/QQ 的信誉库中初始分极低。若你的短链页 HTML 中title写的是“短链接跳转中…”或body里只有Loading...会被风控模型识别为“无实质内容的跳转页”归类为“黑帽SEO工具”。解决在跳转页即redirect.php渲染的页面中添加符合平台规范的“过渡页”HTML!DOCTYPE html htmlheadmeta charsetutf-8title正在跳转.../title meta nameviewport contentwidthdevice-width, initial-scale1.0 stylebody{font-family:Helvetica,sans-serif;text-align:center;padding:50px;}a{color:#09bb07;}/style /headbodyh2正在安全跳转/h2p即将前往a href? htmlspecialchars($longUrl) ?? htmlspecialchars(parse_url($longUrl, PHP_URL_HOST)) ?/a/p/body/html提示这个 HTML 必须在header(Location: ...)之前输出且href使用htmlspecialchars防 XSS。微信要求跳转页有“可读标题可点击链接明确目标域名”否则视为违规。5. 进阶技巧用 Redis 缓存热点短码把 QPS 从 200 提升到 5000当短链日活超过 10 万次MySQL 的单点查询会成为瓶颈。此时不能简单加从库——因为短链查询是典型的“读多写少、Key-Value、高并发、低延迟”场景Redis 是唯一合理选择。但直接用GET/SET会丢失状态控制如status字段我们需要用 Redis Hash 结构把整条记录缓存起来并与 MySQL 保持强一致。5.1 Redis 数据结构设计Hash 存储 过期时间分级我们不用 String 存long_url而用 Hash 存整个记录字段与 MySQL 表对齐Redis KeyFieldValue说明short:abc123long_urlhttps://target.com/xxx原始长链short:abc123status0状态0启用short:abc123hit_count123命中次数Redis 内原子自增short:abc123referer_whitelist[servicewechat.com]JSON 字符串关键点在于过期时间TTL分级设置status0启用的短码TTL 设为8640024 小时status1禁用的短码TTL 设为36001 小时避免长期占用内存每次hit_count自增后调用EXPIRE延长 TTL 到 24 小时保证热点链永不过期。5.2 PHP 缓存读写逻辑双写 降级保障修改redirect.php中的查询部分加入 Redis 读取逻辑。注意必须有降级机制当 Redis 不可用时自动回退到 MySQL 查询避免雪崩。// redirect.php 中查询部分替换为 $redisKey short: . $shortCode; $redis new Redis(); try { $redis-connect(127.0.0.1, 6379, 1); // 1秒超时 $cacheData $redis-hGetAll($redisKey); if (!empty($cacheData) isset($cacheData[status]) $cacheData[status] 0) { // 缓存命中直接使用 $longUrl $cacheData[long_url]; $refererWhitelist json_decode($cacheData[referer_whitelist] ?? [], true) ?: []; // 更新 hit_count 原子操作 $redis-hIncrBy($redisKey, hit_count, 1); $redis-expire($redisKey, 86400); // 延长过期时间 } else { // 缓存未命中或状态异常回退 MySQL $stmt $pdo-prepare(SELECT long_url, status, referer_whitelist FROM short_urls WHERE short_code ? AND status 0); $stmt-execute([$shortCode]); $row $stmt-fetch(); if (!$row) { http_response_code(404); exit(Not found); } $longUrl $row[long_url]; $refererWhitelist json_decode($row[referer_whitelist], true) ?: []; // 写入 Redis 缓存异步不阻塞跳转 $redis-multi(); $redis-hMset($redisKey, [ long_url $longUrl, status 0, hit_count 1, referer_whitelist json_encode($refererWhitelist) ]); $redis-expire($redisKey, 86400); $redis-exec(); } } catch (Exception $e) { // Redis 异常强制降级到 MySQL error_log(Redis error: . $e-getMessage()); $stmt $pdo-prepare(SELECT long_url, status, referer_whitelist FROM short_urls WHERE short_code ? AND status 0); $stmt-execute([$shortCode]); $row $stmt-fetch(); if (!$row) { http_response_code(404); exit(Not found); } $longUrl $row[long_url]; $refererWhitelist json_decode($row[referer_whitelist], true) ?: []; }性能对比实测在 4 核 8G 云服务器上纯 MySQL 查询 QPS 约 200加入 Redis 缓存后QPS 稳定在 4500–5200CPU 使用率从 95% 降至 25%。关键在于hIncrBy和expire的原子性避免了并发更新hit_count的竞态问题。5.3 缓存一致性保障MySQL 更新时同步刷新 Redis光有读缓存不够当运营后台手动禁用某短链UPDATE short_urls SET status 1 WHERE short_code abc123Redis 缓存必须立即失效否则用户仍能跳转。我们用 MySQL 的triggerUDF或更简单的方案在 PHP 后台管理接口中执行 SQL 后主动删除 Redis Key。// admin/disable.php $shortCode $_POST[code]; $pdo-prepare(UPDATE short_urls SET status 1 WHERE short_code ?)-execute([$shortCode]); // 同步清理 Redis $redis-del(short: . $shortCode); // 立即失效提示不要用DEL以外的命令如EXPIRE 1因为DEL是原子删除确保缓存与 DB 状态绝对一致。对于高并发禁用场景可加 Redis 锁SET lock:short:abc123 1 NX EX 10但中小项目通常无需。我上线这套方案后把原来每周都要手动处理 3–5 次的“红链投诉”压缩到每月不到 1 次。核心心得只有一条防红不是对抗平台而是理解它的规则然后用可控的基础设施去适配——短码生成只是起点真正的价值在跳转链路上的每一个可控节点。希望帮到你。本文还有配套的精品资源点击获取