首页 > Technology > 正文

Nginx 配置 HTTPS 全站强制跳转:从证书到混合内容修复

fenij 2026-08-11 43 Technology

SSL 证书签下来之后第一件事:让所有 HTTP 请求都跳到 HTTPS。否则浏览器地址栏出现「不安全」,SEO 排名也会被降权。Nginx 上配这个其实不复杂,但 90% 的人栽在「301 跳转规则写错」或「和 CDN 重复强制」上。

先确认证书就位

Let’s Encrypt、腾讯云免费 DV、Cloudflare Origin Certificate 任选一个都行,关键是:

  • 证书包含主域名和 www 子域名(一张 SAN 多域名证书最方便)
  • 私钥文件权限 600,属主 root
  • 已经测过 openssl s_client -connect yourdomain.com:443 能看到证书链
🔒
SSL 就位
证书 + 私钥
↪️
301 永久
HTTP → HTTPS
🛡️
HSTS
浏览器侧锁定

第一步:HTTPS server 块先写好

不要急着写跳转,先确保 443 端口能正常访问。Nginx 站点配置 /etc/nginx/sites-available/yourdomain.conf 加一段:

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options SAMEORIGIN;
    add_header X-Content-Type-Options nosniff;

    root /var/www/yourdomain;
    index index.html index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
    }
}

关键点:

  • listen 443 ssl http2:开启 HTTPS 2,Chrome 浏览器地址栏会有更快标志
  • ssl_protocols TLSv1.2 TLSv1.3:禁用 TLSv1.0 / 1.1,老浏览器兼容性自己权衡
  • add_header Strict-Transport-Security:HSTS,浏览器以后强制 HTTPS
  • add_header X-Frame-Options / X-Content-Type-Options:基础安全头,顺手加上

第二步:写 HTTP → HTTPS 301 跳转

在同一个配置文件的 server { listen 80; } 块里加:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    # 关键:acme.sh 续签需要 80 端口的特定路径
    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
    }

    # 其他全部跳 HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

用 301 而不是 302——301 是永久跳转,浏览器会缓存、SEO 会把权重完全转过去;302 是临时跳转,每次都要重新请求,对 SEO 不友好。

第三步:测一下配置文件 + reload

sudo nginx -t # 测试配置语法,没问题才继续
sudo systemctl reload nginx

浏览器里访问 http://yourdomain.com,应该立刻跳到 https://yourdomain.com,地址栏有锁图标。

第四步:处理混合内容

页面已经 HTTPS 了,但里面嵌入的 http:// 图片、CSS、JS 还在走老路——这就是「混合内容」,浏览器会过滤甚至阻止。表现:锁图标没了、Network 里 Mixed Content 报错。

⚠️
被动混合内容
图片 / 视频 / 音频(被允许)
🚫
主动混合内容
脚本 / iframe / CSS(被拦截)

修法(按优先级):

  1. 数据库批量替换(WordPress):UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://yourdomain.com', 'https://yourdomain.com');
  2. 主题/插件里的硬编码 URL:用 Search & Replace 插件扫一遍
  3. CDN 资源:协议相对 URL(//cdn.example.com/xxx.js)或显式 https
  4. WordPress 自动替换:在 wp-config.php 加 define('WP_HOME', 'https://yourdomain.com');

第五步:开 HSTS(强烈建议)

HSTS 告诉浏览器:「这个域名以后只能用 HTTPS,连 HTTP 请求都不要发」。第一次访问之后浏览器就记住了,连明文请求都不会发出去,杜绝降级攻击。

Nginx 配置里已经加了 Strict-Transport-Security 头。访问 https://hstspreload.org/ 提交域名,加入浏览器预加载列表,Chrome / Firefox / Safari 一发布就生效。

第六步:CDN 用户的额外步骤

套了 Cloudflare / 阿里云 CDN 的朋友注意:

CDN SSL 模式 含义 是否推荐
Off CDN 不加密 ❌ 别用
Flexible CDN→用户 HTTPS,CDN→源站 HTTP ⚠️ 会引起循环
Full 全程 HTTPS,自签证书也接受 ✅ 可用
Full (Strict) 全程 HTTPS,校验证书合法性 ✅ 强烈推荐

「ERR_TOO_MANY_REDIRECTS」九成就是这个 Flexible 模式引起的——CDN 用 HTTP 回源,源站又强制跳 HTTPS,请求来回死循环。改 Full 或 Full (Strict) 立刻好。

常见问题

Q:开了 HTTPS 后网站变慢了怎么办?
A:HTTPS 本身多 1 次 TLS 握手(100ms 左右),但启用 HTTP/2(listen 443 ssl http2)+ 会话复用(ssl_session_cache)能抵消掉大头。感觉明显变慢多半是网络问题不是 HTTPS 问题。

Q:Let’s Encrypt 证书怎么自动续签?
A:用 certbot 或 acme.sh 自动加 cron。certbot 默认会在 90 天剩余 30 天时自动续签,安装时勾选「Set up cron job」即可。续签日志在 /var/log/letsencrypt/。

Q:HSTS 开了之后还能改回 HTTP 吗?
A:能,但要等浏览器侧的 max-age 过期。安全起见,先确认 HTTPS 完全稳了再开 HSTS,不要一上来就 preload(preload 之后基本撤销要等几个月)。

按这六步把 Nginx 上的 HTTPS 强制跳转和混合内容处理完,地址栏就会一直显示锁图标了。如果哪一步卡住或者 reload 之后网站反而崩了,把 error.log 末尾几行贴到 fenij.com 留言,我帮你接着分析。

相关完整手册

系统化的排查与配置思路,建议顺手收藏这几篇完整手册: