-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy path.nginx.conf.example
More file actions
300 lines (265 loc) · 11.3 KB
/
.nginx.conf.example
File metadata and controls
300 lines (265 loc) · 11.3 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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# ========================================
# Pinakes - Nginx Configuration Example
# ========================================
#
# This file shows how to configure Nginx for Pinakes.
# Copy this to your Nginx sites-available directory and modify as needed.
#
# IMPORTANT: Document root should point to the /public/ directory!
#
# For production: Replace example.com with your domain
# For development: Use localhost
#
# ========================================
server {
listen 80;
listen [::]:80;
# Replace with your domain
server_name example.com;
# IMPORTANT: Document root must point to the PUBLIC directory
root /var/www/pinakes/public;
index index.php;
charset utf-8;
# Max upload size (adjust as needed for your library)
client_max_body_size 100M;
# ========================================
# Logging
# ========================================
access_log /var/log/nginx/pinakes_access.log;
error_log /var/log/nginx/pinakes_error.log;
# ========================================
# Security Headers
# ========================================
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Uncomment and adjust CSP for production:
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: https: http: blob:; connect-src 'self'; frame-src 'self' https://www.openstreetmap.org; frame-ancestors 'self';" always;
# ========================================
# Block access to sensitive/hidden files
# ========================================
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# ========================================
# Plugin-served assets (MUST precede the generic static block)
# ========================================
# Plugins such as digital-library and archives stream their own CSS/JS
# through Slim routes (/plugins/<name>/assets/<type>/<file>). These files
# don't exist on disk — PHP reads them from the plugin package and sets
# its own Cache-Control header (typically `public, max-age=31536000`
# because plugin asset paths are versioned by the plugin manifest).
#
# If the generic static location below catches these requests first, its
# `add_header Cache-Control "public, max-age=2592000, must-revalidate"`
# would stack on top of the plugin's header, sending the browser two
# conflicting values.
# Route them straight to PHP so only the plugin's header reaches the wire.
location ^~ /plugins/ {
try_files $uri /index.php?$query_string;
}
# ========================================
# Static assets with caching
# ========================================
# Note: Security headers must be repeated here because Nginx doesn't inherit
# add_header directives from parent context when add_header is used in a location
#
# Miss behaviour: hard 404. The plugin-asset location above already
# forwards `/plugins/*` to PHP; `/uploads/*` has its own block below;
# anything else with these extensions that isn't on disk is a genuine
# 404, not something PHP needs to answer. Falling back to index.php
# here would waste a PHP-FPM worker per bot/scanner probe and fill
# app logs with noise.
#
# NOTE on caching: `immutable` would tell browsers never to revalidate for
# `expires`. Since Pinakes doesn't version-fingerprint core assets either,
# setting `immutable` + `1y` means asset updates stay invisible for up
# to a year. We use a conservative 1-month TTL with `must-revalidate`
# so browsers recheck on next hit after expiry.
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|mp3|mp4|webm|pdf|epub)$ {
# Disable the `expires` auto-emitted Cache-Control and write a single
# explicit header instead. `expires 1M` would synthesise its own
# `Cache-Control: max-age=2592000`, which stacked on top of our
# `add_header Cache-Control "..."` would send the browser two
# concatenated values and make the effective policy ambiguous.
expires off;
# No `always` flag on Cache-Control: nginx then applies this header
# only to 2xx/3xx responses. A transient 404 emitted by `try_files
# ... =404` below must NOT be cached for a month — the next deploy
# or upload should let users recover. Security headers keep
# `always` intentionally (they need to be present on error pages
# too, defense-in-depth against MIME sniffing / clickjacking).
add_header Cache-Control "public, max-age=2592000, must-revalidate";
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
try_files $uri =404;
}
# ========================================
# Uploads directory
# ========================================
location ^~ /uploads/ {
# Serve uploaded files directly
try_files $uri =404;
# CORS headers for media streaming (Digital Library plugin)
# Note: Security headers must be repeated here (Nginx doesn't inherit add_header)
location ~* \.(mp3|m4a|ogg|wav|mp4|webm|ogv)$ {
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
add_header Access-Control-Allow-Headers "Range, Content-Type" always;
add_header Access-Control-Expose-Headers "Content-Length, Content-Range, Accept-Ranges" always;
add_header Accept-Ranges bytes always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
try_files $uri =404;
}
}
# ========================================
# Installer (accessible at /installer/)
# ========================================
# SECURITY: Remove or comment out this block after installation is complete.
# Access is restricted to localhost only as a safety measure.
location ^~ /installer/ {
# Only allow access from localhost (loopback)
allow 127.0.0.1;
allow ::1;
deny all;
alias /var/www/pinakes/installer/;
index index.php;
# Static assets for installer
location ~* ^/installer/.*\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
try_files $uri =404;
}
# PHP processing for installer
location ~ ^/installer/.*\.php$ {
# Only allow index.php
if ($uri !~ "^/installer/index\.php$") {
return 403;
}
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
# Important: Use $request_filename with alias
fastcgi_param SCRIPT_FILENAME $request_filename;
include fastcgi_params;
fastcgi_read_timeout 300;
}
# Route to installer index.php
try_files $uri $uri/ /installer/index.php?$query_string;
}
# ========================================
# PHP Processing
# ========================================
location ~ \.php$ {
# Only allow index.php for security
if ($fastcgi_script_name !~ "^/index\.php$") {
return 403;
}
# Adjust PHP-FPM socket/port to match your setup:
# - Debian/Ubuntu: unix:/var/run/php/php8.2-fpm.sock
# - CentOS/RHEL: unix:/var/run/php-fpm/www.sock
# - macOS (Homebrew): 127.0.0.1:9000
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# Pass Authorization header (for API authentication)
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
# Increase timeouts for long operations (imports, backups)
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
# PHP settings (can also be set in php.ini)
fastcgi_param PHP_VALUE "upload_max_filesize=100M \n post_max_size=100M \n max_execution_time=300";
}
# ========================================
# Front Controller
# ========================================
location / {
# Try to serve file directly, fall back to index.php
try_files $uri $uri/ /index.php?$query_string;
}
}
# ========================================
# Installer Configuration (Separate Server Block)
# ========================================
# The installer runs from /installer/ directory outside public.
# After installation, you can remove or comment out this block.
#
# Access installer at: http://example.com:8889/
#
server {
# SECURITY: Limit installer to loopback interface only
# Never expose installer to public network
listen 127.0.0.1:8889;
listen [::1]:8889;
server_name localhost;
# Point to the installer directory (NOT public!)
root /var/www/pinakes/installer;
index index.php;
charset utf-8;
client_max_body_size 100M;
# Static assets for installer
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
try_files $uri =404;
}
# Block hidden files
location ~ /\. {
deny all;
}
# PHP processing for installer
location ~ \.php$ {
if ($fastcgi_script_name !~ "^/index\.php$") {
return 403;
}
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_read_timeout 300;
}
# Route to front controller
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
# ========================================
# SSL/HTTPS Configuration (Recommended for Production)
# ========================================
# Uncomment and configure for production with SSL certificate.
# Use Let's Encrypt (certbot) for free certificates.
#
# server {
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
#
# server_name example.com;
#
# # SSL Certificate paths (Let's Encrypt example)
# ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
#
# # Modern SSL configuration
# 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;
#
# # HSTS (optional but recommended)
# add_header Strict-Transport-Security "max-age=63072000" always;
#
# # ... (copy the location blocks from the HTTP server above)
#
# root /var/www/pinakes/public;
# index index.php;
# # ... etc
# }
#
# # Redirect HTTP to HTTPS
# server {
# listen 80;
# listen [::]:80;
# server_name example.com;
# return 301 https://$server_name$request_uri;
# }