0.什么是Nginx?
Nginx(发音为 "engine-x")是一款高性能的 HTTP 服务器和反向代理服务器,同时也可用作邮件代理服务器和通用的 TCP/UDP 代理服务器。它由俄罗斯工程师 Igor Sysoev 于 2002 年开始开发,2004 年首次公开发布,旨在解决当时流行的 Apache HTTP Server 在高并发场景下的性能瓶颈。如今,Nginx 已成为全球最流行的 Web 服务器之一,被大量高流量网站(如 Netflix、GitHub、WordPress.com)所采用。
0.1 核心特性
-
高并发、低内存占用
Nginx 采用异步、非阻塞的事件驱动架构,能够在单台服务器上处理数以万计的并发连接,而内存消耗远低于传统的基于进程/线程的模型。 -
反向代理与负载均衡
作为反向代理,Nginx 可以将客户端请求分发到后端服务器(如应用服务器、数据库),并支持多种负载均衡算法(轮询、最少连接、IP 哈希等),实现高可用和水平扩展。 -
静态文件服务
Nginx 在处理静态文件(HTML、CSS、JavaScript、图片等)方面非常高效,能够直接发送文件而不占用过多的内存和 CPU。 -
SSL/TLS 终止
支持 HTTP/2、HTTPS,可以集中管理 SSL 证书,对后端服务透明,减轻后端服务的加密负担。 -
缓存机制
内置强大的缓存功能,可以缓存后端响应,加速内容分发,降低后端压力。 -
模块化设计
核心功能精简,通过模块扩展。官方提供了丰富的模块(如ngx_http_proxy_module、ngx_http_ssl_module),也支持第三方模块。 -
高可靠性
主进程与工作进程分离,主进程负责管理,工作进程处理请求。即使某个工作进程崩溃,主进程也能立即启动新的工作进程,保证服务持续可用。
0.2 架构解析
Nginx 的架构基于 事件驱动(event-driven) 和 非阻塞(non-blocking) 模型,具体实现依赖于操作系统的高效机制(如 Linux 的 epoll、FreeBSD 的 kqueue)。其进程模型如下:
- Master 进程:负责读取配置文件、绑定端口、启动和监控 Worker 进程。不处理具体请求。
- Worker 进程:通常每个 CPU 核心对应一个 Worker 进程。Worker 进程接受连接、处理请求,通过事件循环来管理多个连接。
每个 Worker 进程可以处理成千上万个连接,因为它通过事件通知机制在单个线程中处理所有并发 I/O 操作,而不是为每个连接创建独立的进程/线程。
这种设计使得 Nginx 在处理大量长连接(如 WebSocket、HTTP/2)时表现出色。
1.什么是反向代理?
在搞清楚反向代理之前,我们了解一下正向代理。
所谓正向代理,就是当用户访问原本无法访问网站A(比如google)时,我们首先通过某个代理服务器或某个代理池充当中介从而隐藏自己真实的ip,同时因为“中介”能够直接访问网站A,所以我们可以间接地访问到网站A。生活中常见的类似例子有路由器转发、开代理池的编程、缓存、上网行为认证等。
那么反向代理是什么呢?简单来说,就是当出差在外的用户突然需要访问单位A(比如学校)的内网时,必须先经过该单位的内部代理服务器,进而转发需要传达的消息到内部的后端服务器,从而使得学校内部或公司内部的相关人员接受到准确的消息。
什么时候需要用到反向代理呢?其中一个好处是当一个公司同时部署了多个服务器时,对于发送的指定消息只需要访问其中一个或少量服务器时,如果没有反向代理服务器,在公网上这些消息无法确定被发送到那个服务器,从而造成大量的浪费资源的行为。但是,如果拥有反向代理服务器,这时候被指定的消息可能需要通过内部代理服务器来进行简单的转发从而达到负载均衡的目的。否则,当内部人员的消息内容过多或者来自外部的攻击行为过于频繁,这时候多个服务器可能面临瘫痪、崩溃的局面。另外一个好处就是,当使用Ngnix/Lua等反向代理技术时,这些内部服务器无法暴露在公网上,很好的隐藏了自己的真实ip,从而公网用户无法攻击内部服务器。同时利用反向代理技术可以迅速有效地部署防火墙产品,譬如AWS、GCP、宝塔、雷池等,这些防火墙有效地保护了个人数字资产。
总结来说,就正如前面的表格比较和下方文字描述的那样。
2.常见的配置模块
这些常用模块都是由ai画图软件总结的,大家阅读的时候可能对英语阅读能力较强,笔者建议读者结合官方文档做一个参考。
2.1 SSL/TLS证书模块
server {
listen 443 ssl http2;
server_name example.com;
# SSL Certificate files
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# SSL Protocols and Ciphers
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# SSL Session Cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# HSTS (HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://backend;
}
}
2.2 gzip on 模块
http {
# Enable Gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 1000;
# Specify MIME types to compress
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/rss+xml
font/truetype
font/opentype
application/vnd.ms-fontobject
image/svg+xml;
# Disable gzip for IE6
gzip_disable "msie6";
server {
listen 80;
server_name example.com;
# ... rest of config
}
}
2.3 代理池模块
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}
server {
listen 80;
server_name example.com;
location / {
# Basic proxy settings
proxy_pass http://backend;
# Headers
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;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Error handling
proxy_next_upstream error timeout invalid_header http_500;
}
}
2.4 重写模块
server {
listen 80;
server_name example.com www.example.com;
# Redirect www to non-www
if ($host = www.example.com) {
return 301 https://example.com$request_uri;
}
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
# Rewrite old URLs to new ones
rewrite ^/old-page$ /new-page permanent;
rewrite ^/blog/(.*)$ /articles/$1 permanent;
# Conditional rewrites
location /api {
# Rewrite /api/v1/users to /users
rewrite ^/api/v1/(.*)$ /$1 break;
proxy_pass http://backend;
}
# Use try_files for clean URLs
location / {
try_files $uri $uri/ /index.html;
}
}
2.5 响应头模块
server {
listen 80;
server_name example.com;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
# Remove headers (for security)
more_clear_headers 'Server';
more_clear_headers 'X-Powered-By';
# CORS Headers
location /api {
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://backend;
}
# Cache Control
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
2.6 速率限制模块
http {
# Define rate limit zones
# Zone 'one' allows 10 requests per second per IP
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
# Zone 'api' allows 100 requests per minute per IP
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/m;
# Zone 'login' allows 5 requests per minute per IP
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
server {
listen 80;
server_name example.com;
# General rate limiting
location / {
limit_req zone=one burst=20 nodelay;
proxy_pass http://backend;
}
# API rate limiting
location /api {
limit_req zone=api burst=50;
limit_req_status 429;
proxy_pass http://backend;
}
# Strict rate limiting for login
location /login {
limit_req zone=login burst=5;
limit_req_log_level warn;
proxy_pass http://backend;
}
}
}
2.7 镜像模块
http {
# Define cache path and settings
proxy_cache_path /var/cache/nginx/data
levels=1:2
keys_zone=my_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
listen 80;
server_name example.com;
location / {
# Enable caching
proxy_cache my_cache;
# Cache status in response header (for debugging)
add_header X-Cache-Status $upstream_cache_status;
# Cache valid responses
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
# Cache even with certain headers
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# Cache locking
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
# Cache key
proxy_cache_key "$scheme$request_method$host$request_uri";
# Bypass cache for certain conditions
proxy_cache_bypass $http_pragma $http_authorization;
proxy_no_cache $http_pragma $http_authorization;
proxy_pass http://backend;
}
# Don't cache API POST requests
location /api {
proxy_cache_methods GET HEAD;
proxy_pass http://backend;
}
}
}
2.8 基础认证模块
server {
listen 80;
server_name example.com;
# Protect entire site
location / {
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend;
}
# Protect specific directory
location /admin {
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd_admin;
proxy_pass http://backend;
}
# Public location (no auth)
location /public {
auth_basic off;
proxy_pass http://backend;
}
# Conditional authentication (IP-based)
location /restricted {
satisfy any;
# Allow from specific IPs without password
allow 192.168.1.0/24;
allow 10.0.0.0/8;
deny all;
# Require password if not from allowed IP
auth_basic "Restricted Access";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend;
}
}
# Create password file with:
# htpasswd -c /etc/nginx/.htpasswd username
2.9 geoIP 模块
http {
# Define geo-based variables
geo $country {
default ZZ;
# US IP ranges
1.2.3.0/24 US;
5.6.7.0/24 US;
# UK IP ranges
10.20.30.0/24 UK;
# Include external file
include /etc/nginx/geo.conf;
}
# Create blocking rules based on country
geo $blocked_country {
default 0;
1.2.3.0/24 1; # Block this range
}
# Map based on geo
map $country $redirect_domain {
default example.com;
US us.example.com;
UK uk.example.com;
DE de.example.com;
}
server {
listen 80;
server_name example.com;
# Block specific countries
if ($blocked_country) {
return 403 "Access denied from your location";
}
# Redirect based on country
location / {
# Use country in headers
add_header X-Country-Code $country;
# Country-specific content
root /var/www/$country;
try_files $uri $uri/ /index.html;
}
# API endpoint that logs country
location /api {
proxy_set_header X-Country $country;
proxy_pass http://backend;
}
}
}
2.10 upstream模块
http {
# Round-robin load balancing (default)
upstream backend_round_robin {
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}
# Least connections load balancing
upstream backend_least_conn {
least_conn;
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}
# IP hash (session persistence)
upstream backend_ip_hash {
ip_hash;
server backend1.example.com:8080;
server backend2.example.com:8080;
server backend3.example.com:8080;
}
# Weighted load balancing
upstream backend_weighted {
server backend1.example.com:8080 weight=3;
server backend2.example.com:8080 weight=2;
server backend3.example.com:8080 weight=1;
}
# Advanced configuration
upstream backend_advanced {
least_conn;
# Primary servers
server backend1.example.com:8080 weight=5 max_fails=3 fail_timeout=30s;
server backend2.example.com:8080 weight=3 max_fails=3 fail_timeout=30s;
# Backup server (used when primary servers are down)
server backup.example.com:8080 backup;
# Keep alive connections
keepalive 32;
keepalive_timeout 60s;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_advanced;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
}
3.常见的配置错误
3.1 错误的分号
server {
listen 80
server_name example.com
location / {
proxy_pass http://backend
}
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
}
}
3.2 错误的if 声明
location / {
# WRONG: Using if for redirects
if ($request_uri ~* "^/old-url") {
proxy_pass http://backend;
}
# WRONG: Multiple conditions
if ($host != "example.com") {
rewrite ^ https://example.com$request_uri permanent;
}
}
# RIGHT: Use return for redirects
location = /old-url {
return 301 /new-url;
}
# RIGHT: Use separate server block
server {
listen 80;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 80;
server_name example.com;
# ... normal config
}
3.3 错误的尾部斜杠传递
# This keeps the /api prefix
location /api {
proxy_pass http://backend; # Wrong if you want to strip /api
}
# This might cause double slashes
location /api/ {
proxy_pass http://backend/; # Creates //api/endpoint
}
# Strip /api prefix from request
location /api/ {
proxy_pass http://backend/; # /api/users -> /users
}
# Keep /api prefix in request
location /api {
proxy_pass http://backend; # /api/users -> /api/users
}
# Rewrite to remove prefix
location /api {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://backend;
}
3.4 缺失根目录与别名理解
# WRONG: Using root when alias is needed
location /static {
root /var/www/assets; # Looks for /var/www/assets/static
}
# WRONG: Using alias without trailing slash
location /images {
alias /var/www/pics; # Should have trailing slash
}
# RIGHT: Using root (appends location to path)
location /static {
root /var/www; # Serves /var/www/static
}
# RIGHT: Using alias (replaces location in path)
location /static {
alias /var/www/assets/; # Serves /var/www/assets/
}
# Alternative with root
location /static {
root /var/www;
# Serves /var/www/static/
}
3.5 重复的请求头
http {
add_header X-Frame-Options "DENY";
server {
add_header X-Frame-Options "SAMEORIGIN"; # Duplicate!
location / {
add_header X-Custom-Header "value";
proxy_pass http://backend;
# Header won't be added because proxy_pass clears headers
}
}
}
http {
# Don't add headers here unless needed globally
server {
# Use 'always' to add headers even for error responses
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
location / {
# Headers in location are inherited
proxy_pass http://backend;
# Add location-specific headers
add_header X-Custom-Header "value" always;
}
}
}
3.6 错误的正则表达式
# Case-sensitive match (might miss .JPG, .PNG)
location ~ \.(jpg|png|gif)$ {
expires 30d;
}
# Wrong: Using = with regex
location = ~ /api {
proxy_pass http://backend;
}
# Case-insensitive match for file extensions
location ~* \.(jpg|jpeg|png|gif|ico|svg)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Exact match (fastest)
location = /api {
proxy_pass http://backend;
}
# Regex match (case-sensitive)
location ~ ^/api/v[0-9]+ {
proxy_pass http://backend;
}
# Regex match (case-insensitive)
location ~* ^/API {
proxy_pass http://backend;
}
3.7 错误的缓冲区大小
location / {
# Too small buffers
proxy_buffer_size 1k;
proxy_buffers 4 1k;
# Inconsistent sizes
client_body_buffer_size 128k;
client_max_body_size 1m; # Should be larger
}
http {
# Appropriate buffer sizes
client_body_buffer_size 128k;
client_max_body_size 10m;
client_header_buffer_size 1k;
large_client_header_buffers 4 8k;
}
location / {
# Proxy buffer configuration
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
proxy_pass http://backend;
}
3.8 服务器名称哈希桶大小
http {
# Missing hash bucket size with many server names
server {
server_name example.com www.example.com api.example.com cdn.example.com static.example.com;
}
server {
server_name another-very-long-domain-name.com www.another-very-long-domain-name.com;
}
}
# Nginx may fail to start with error about hash bucket size
http {
# Set appropriate hash bucket size
server_names_hash_bucket_size 64;
server_names_hash_max_size 512;
server {
server_name example.com www.example.com api.example.com cdn.example.com static.example.com;
# ... config
}
server {
server_name another-very-long-domain-name.com www.another-very-long-domain-name.com;
# ... config
}
}
**💡 Explanation:** When you have many or long server names, increase the hash bucket size to avoid startup errors.
3.9 错误的变量环境
# WRONG: Variables not allowed in upstream
upstream backend {
server $backend_host:8080; # Won't work!
}
# WRONG: Variables in ssl_certificate
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/$host.crt; # Won't work!
ssl_certificate_key /etc/nginx/ssl/$host.key;
}
# RIGHT: Use map to set backend
map $host $backend_server {
default backend1.example.com:8080;
api.example.com backend2.example.com:8080;
}
server {
location / {
proxy_pass http://$backend_server;
}
}
# RIGHT: Use separate server blocks for different SSL certs
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
}
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/api.example.com.crt;
ssl_certificate_key /etc/nginx/ssl/api.example.com.key;
}
3.10 缺失的上游定义
server {
listen 80;
server_name example.com;
location / {
# ERROR: 'backend' upstream not defined
proxy_pass http://backend;
}
}
upstream backend {
server backend1.example.com:8080;
server backend2.example.com:8080;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
}
}
# OR: Use direct server address
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend1.example.com:8080;
}
}
3.11 错误的try_files用法
location / {
# WRONG: Missing final fallback
try_files $uri $uri/;
# WRONG: Using proxy_pass as fallback (doesn't work)
try_files $uri $uri/ proxy_pass http://backend;
}
location / {
# RIGHT: Proper fallback to index file
try_files $uri $uri/ /index.html;
}
location / {
# RIGHT: Fallback to named location
try_files $uri $uri/ @backend;
}
location @backend {
proxy_pass http://backend;
}
location /api {
# Don't use try_files with proxy_pass in same location
proxy_pass http://backend;
}
3.12 重写循环
server {
listen 80;
server_name example.com;
location / {
# INFINITE LOOP: Rewrites to itself
rewrite ^/(.*)$ /$1 permanent;
}
# Another loop example
location /old {
rewrite ^/old$ /new permanent;
}
location /new {
rewrite ^/new$ /old permanent;
}
}
server {
listen 80;
server_name example.com;
location = /old-page {
return 301 /new-page;
}
location / {
# Use 'last' or 'break' to stop rewriting
rewrite ^/api/v1/(.*)$ /api/v2/$1 last;
proxy_pass http://backend;
}
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
# ... SSL config and normal operations
}
4.nginx部署tomcat实例
本部分已经有非常好的示例,建议参考51CTO或知乎。即参考文献3,4.
参考文献