一、目标结构h2
本文用于将一个本机端口上的 Web 服务,通过 Nginx 反向代理到指定域名,并配置 HTTPS。
目标结构:
https://<SERVICE_DOMAIN> ↓Nginx 443 ↓http://127.0.0.1:<BACKEND_PORT> ↓后端服务示例占位符:
<SERVICE_DOMAIN> = app.example.com<BACKEND_PORT> = 18090<SERVICE_NAME> = app二、前置检查h2
确认域名已经解析到当前服务器:
dig +short <SERVICE_DOMAIN>curl -4s ifconfig.me两者返回的 IP 应该一致。
确认后端服务在服务器本机可访问:
curl -I http://127.0.0.1:<BACKEND_PORT>正常情况下应返回类似:
HTTP/1.1 200 OK或者:
HTTP/1.1 302 FoundHTTP/1.1 401 Unauthorized只要能返回 HTTP 响应,就说明后端服务可用。
确认端口监听:
ss -lntp | grep <BACKEND_PORT>三、安装 Nginx 和 Certboth2
apt updateapt install -y nginx certbot python3-certbot-nginxsystemctl enable --now nginx放行 80 和 443:
ufw allow 80/tcpufw allow 443/tcp如果不用 ufw,就在服务器安全组或防火墙中放行:
TCP 80TCP 443四、创建 HTTP 临时反代配置h2
先创建 HTTP 配置,用于测试反代和申请证书。
cat > /etc/nginx/sites-available/<SERVICE_DOMAIN> <<'EOF'server { listen 80; listen [::]:80;
server_name <SERVICE_DOMAIN>;
access_log /var/log/nginx/<SERVICE_DOMAIN>.access.log; error_log /var/log/nginx/<SERVICE_DOMAIN>.error.log;
location / { proxy_pass http://127.0.0.1:<BACKEND_PORT>;
proxy_http_version 1.1;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_connect_timeout 10s; proxy_send_timeout 60s; proxy_read_timeout 60s; }}EOF启用站点:
ln -sf /etc/nginx/sites-available/<SERVICE_DOMAIN> /etc/nginx/sites-enabled/<SERVICE_DOMAIN>测试配置:
nginx -t通过后重载:
systemctl reload nginx测试 HTTP 访问:
curl -I http://<SERVICE_DOMAIN>如果返回后端服务响应,说明 HTTP 反代正常。
五、申请 HTTPS 证书h2
certbot --nginx -d <SERVICE_DOMAIN>如果提示是否将 HTTP 跳转到 HTTPS,建议选择跳转。
测试证书续期:
certbot renew --dry-run六、最终 HTTPS 反代配置h2
证书申请成功后,覆盖成最终配置。
cat > /etc/nginx/sites-available/<SERVICE_DOMAIN> <<'EOF'upstream <SERVICE_NAME>_backend { server 127.0.0.1:<BACKEND_PORT>; keepalive 32;}
server { listen 80; listen [::]:80;
server_name <SERVICE_DOMAIN>;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 301 https://$host$request_uri; }}
server { listen 443 ssl; listen [::]:443 ssl;
http2 on;
server_name <SERVICE_DOMAIN>;
ssl_certificate /etc/letsencrypt/live/<SERVICE_DOMAIN>/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/<SERVICE_DOMAIN>/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
access_log /var/log/nginx/<SERVICE_DOMAIN>.access.log; error_log /var/log/nginx/<SERVICE_DOMAIN>.error.log;
add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;
gzip on; gzip_comp_level 5; gzip_min_length 1024; gzip_vary on; gzip_proxied any; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml image/svg+xml;
location ~* \.(css|js|mjs|png|jpg|jpeg|gif|ico|svg|webp|woff|woff2|ttf)$ { proxy_pass http://<SERVICE_NAME>_backend;
proxy_http_version 1.1; proxy_set_header Connection "";
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port;
proxy_redirect off;
expires 7d; add_header Cache-Control "public, max-age=604800" always; }
location / { proxy_pass http://<SERVICE_NAME>_backend;
proxy_http_version 1.1; proxy_set_header Connection "";
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port;
proxy_redirect off;
proxy_connect_timeout 10s; proxy_send_timeout 60s; proxy_read_timeout 60s;
client_max_body_size 20m; }}EOF启用并重载:
ln -sf /etc/nginx/sites-available/<SERVICE_DOMAIN> /etc/nginx/sites-enabled/<SERVICE_DOMAIN>nginx -tsystemctl reload nginx七、验证结果h2
检查 HTTP 是否跳转 HTTPS:
curl -I http://<SERVICE_DOMAIN>预期结果:
HTTP/1.1 301 Moved PermanentlyLocation: https://<SERVICE_DOMAIN>/检查 HTTPS 是否正常:
curl -I https://<SERVICE_DOMAIN>预期结果:
HTTP/2 200或者:
HTTP/1.1 200 OK检查后端服务:
curl -I http://127.0.0.1:<BACKEND_PORT>八、常见错误h2
8.1 proxy_pass 报错h3
错误示例:
invalid number of arguments in "proxy_pass" directive正确写法:
proxy_pass http://127.0.0.1:<BACKEND_PORT>;proxy_pass 这一行后面不要加任何多余内容。
错误写法:
proxy_pass http://127.0.0.1:<BACKEND_PORT>; 其他文字8.2 花括号错误h3
错误示例:
unexpected "}"查看配置行号:
nl -ba /etc/nginx/sites-enabled/<SERVICE_DOMAIN>检查每一个 { 是否都有对应的 }。
8.3 HTTP 正常,HTTPS 到了其他站点h3
表现:
curl -I http://<SERVICE_DOMAIN>正常返回后端服务。
但是:
curl -kI https://<SERVICE_DOMAIN>返回了其他站点页面。
原因通常是该域名还没有独立的 443 配置,或者证书配置没有生效。
处理:
certbot --nginx -d <SERVICE_DOMAIN>nginx -tsystemctl reload nginx8.4 无痕模式正常,普通模式跳错h3
通常是浏览器缓存了旧的 301、HSTS 或站点数据。
处理方向:
清理该域名的站点数据清理浏览器 DNS 缓存清理 HSTS 记录重新打开浏览器Chrome / Edge 可检查:
chrome://settings/siteDataedge://settings/siteDataHSTS 页面:
chrome://net-internals/#hstsedge://net-internals/#hsts九、性能测试h2
测试后端响应:
curl -o /dev/null -s -w "DNS:%{time_namelookup}s Connect:%{time_connect}s TTFB:%{time_starttransfer}s Total:%{time_total}s\n" http://127.0.0.1:<BACKEND_PORT>测试 HTTPS 响应:
curl -o /dev/null -s -w "DNS:%{time_namelookup}s Connect:%{time_connect}s TLS:%{time_appconnect}s TTFB:%{time_starttransfer}s Total:%{time_total}s\n" https://<SERVICE_DOMAIN>如果服务器本机测试很快,但浏览器访问感觉慢,通常是客户端到服务器线路、浏览器缓存或页面资源加载问题。
十、FRP 场景h2
如果后端服务是通过 FRP 穿透到服务器本机端口:
服务器本机端口:<FRP_REMOTE_PORT>内网服务端口:<LOCAL_SERVICE_PORT>Nginx 中直接反代:
proxy_pass http://127.0.0.1:<FRP_REMOTE_PORT>;FRPC 示例:
serverAddr = "<VPS_PUBLIC_IP>"serverPort = 7000
auth.method = "token"auth.token = "<FRP_TOKEN>"
transport.tcpMux = truetransport.tcpMuxKeepaliveInterval = 30
[[proxies]]name = "<PROXY_NAME>"type = "tcp"localIP = "127.0.0.1"localPort = <LOCAL_SERVICE_PORT>remotePort = <FRP_REMOTE_PORT>transport.useCompression = true重启 frpc:
systemctl restart frpc服务器检查端口:
ss -lntp | grep <FRP_REMOTE_PORT>curl -I http://127.0.0.1:<FRP_REMOTE_PORT>十一、日志和维护h2
查看访问日志:
tail -f /var/log/nginx/<SERVICE_DOMAIN>.access.log查看错误日志:
tail -f /var/log/nginx/<SERVICE_DOMAIN>.error.log检查 Nginx 配置:
nginx -t重载 Nginx:
systemctl reload nginx检查证书续期:
certbot renew --dry-run