-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnginx.conf
More file actions
197 lines (163 loc) · 6.06 KB
/
Copy pathnginx.conf
File metadata and controls
197 lines (163 loc) · 6.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# Production Nginx Configuration for Flask Portfolio
# Optimized for Waitress/Gunicorn with caching, compression, and security
# Upstream Flask application servers
upstream flask_portfolio {
# Use Waitress on port 8000
server 127.0.0.1:8000 fail_timeout=0;
# For multiple workers (uncomment if needed):
# server 127.0.0.1:8001 fail_timeout=0;
# server 127.0.0.1:8002 fail_timeout=0;
}
# Rate limiting zones
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=api:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=admin:10m rate=2r/s;
# Cache configuration
proxy_cache_path /var/cache/nginx/portfolio levels=1:2 keys_zone=portfolio_cache:10m max_size=100m inactive=60m use_temp_path=off;
# HTTP -> HTTPS redirect
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
# ACME challenge for Let's Encrypt
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# Redirect all HTTP to HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
# SSL Configuration
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.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 10m;
ssl_stapling on;
ssl_stapling_verify on;
# Security headers (CSP handled by Flask app)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Logging
access_log /var/log/nginx/portfolio_access.log;
error_log /var/log/nginx/portfolio_error.log;
# Client body size limit (for uploads)
client_max_body_size 10M;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Root directory (not used, but good to have)
root /var/www/portfolio;
# Static files with aggressive caching
location /static/ {
alias /var/www/portfolio/static/;
# Cache CSS/JS with versioning
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# Cache images
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 1M;
add_header Cache-Control "public";
access_log off;
}
# Cache fonts
location ~* \.(woff|woff2|ttf|otf|eot)$ {
expires 1M;
add_header Cache-Control "public";
access_log off;
}
# Compression
gzip on;
gzip_vary on;
gzip_types text/css application/javascript image/svg+xml;
gzip_min_length 1024;
}
# Admin routes - rate limited
location /admin/ {
limit_req zone=admin burst=5 nodelay;
proxy_pass http://flask_portfolio;
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;
# No caching for admin
proxy_no_cache 1;
proxy_cache_bypass 1;
}
# API routes - rate limited
location /api/ {
limit_req zone=api burst=10 nodelay;
proxy_pass http://flask_portfolio;
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;
# Cache API responses briefly
proxy_cache portfolio_cache;
proxy_cache_valid 200 5m;
proxy_cache_key "$scheme$request_method$host$request_uri";
add_header X-Cache-Status $upstream_cache_status;
}
# CSP violation reporting endpoint
location /csp-report {
proxy_pass http://flask_portfolio;
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;
# No caching
proxy_no_cache 1;
proxy_cache_bypass 1;
}
# All other routes
location / {
limit_req zone=general burst=20 nodelay;
proxy_pass http://flask_portfolio;
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;
# Cache public pages
proxy_cache portfolio_cache;
proxy_cache_valid 200 10m;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_bypass $cookie_session;
proxy_no_cache $cookie_session;
add_header X-Cache-Status $upstream_cache_status;
# Handle WebSocket upgrade (if needed for future features)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Deny access to sensitive files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location ~* (\.git|\.env|\.db|requirements\.txt|README\.md)$ {
deny all;
access_log off;
log_not_found off;
}
# Health check endpoint (bypass rate limiting)
location /health {
proxy_pass http://flask_portfolio;
access_log off;
}
}