Last active
January 23, 2023 09:31
-
-
Save MayankFawkes/2440fe8dae89b2f6a2d80aa4481c4bc4 to your computer and use it in GitHub Desktop.
Personal CDN with Nginx and Golang | in nginx.conf you can add 443 with SSL if you want | thanks to https://bit.ly/3XM7wQm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"net/http" | |
"io/ioutil" | |
"log" | |
"fmt" | |
) | |
var print = fmt.Println; | |
func send(url string) ([]byte, *http.Response, error) { | |
resp, err := http.Get(url) | |
if err!=nil { | |
log.Println(err) | |
return []byte{}, resp, err | |
} | |
defer resp.Body.Close() | |
body, err := ioutil.ReadAll(resp.Body) | |
if err != nil { | |
log.Println(err) | |
return []byte{}, resp, err | |
} | |
return body, resp, nil | |
} | |
func main() { | |
http.HandleFunc("/", HelloServer) | |
log.Println("Server listening on http://0.0.0.0:8000 or http://127.0.0.1:8000") | |
http.ListenAndServe(":8000", nil) | |
} | |
func indexResponse(w http.ResponseWriter) { | |
w.WriteHeader(http.StatusOK) | |
fmt.Fprintf(w, "Welcome to CDN Proxy Node, I only support HTTPS.") | |
} | |
func notFoundResponse(w http.ResponseWriter) { | |
w.WriteHeader(http.StatusNotFound) | |
fmt.Fprintf(w, "Invalid URL") | |
} | |
func HelloServer(w http.ResponseWriter, r *http.Request) { | |
var path string = r.URL.Path[1:] | |
if path == "" { | |
indexResponse(w) | |
return | |
} | |
var url string | |
if path == "favicon.ico" { | |
url = "https://www.google.com/favicon.ico" | |
} else { | |
url = "https://" + r.URL.Path[1:] | |
} | |
body, resp, err := send(url) | |
if err != nil { | |
notFoundResponse(w) | |
return | |
} | |
for key, value := range resp.Header { | |
if key == "Set-Cookie" || key == "content-type" { | |
continue | |
} | |
w.Header().Set(key, value[0]) | |
} | |
w.Header().Set("x-server", "Hekor-Boi-CDN/1337") | |
w.Header().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"", url)) | |
log.Println(url, resp.Status) | |
w.WriteHeader(resp.StatusCode) | |
w.Write(body) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
load_module modules/ngx_http_headers_more_filter_module.so; | |
worker_processes 1; | |
worker_rlimit_nofile 100000; | |
pcre_jit on; | |
events { | |
use epoll; | |
worker_connections 16000; | |
multi_accept on; | |
} | |
http { | |
log_format main '$remote_addr - $remote_user [$time_local] $status ' | |
'"$request" $body_bytes_sent "$http_referer" ' | |
'"$http_user_agent" "$http_x_forwarded_for"'; | |
# IP whitelist to which no conn/rate restrictions should be applied | |
geo $ip_whitelist { | |
default 0; | |
127.0.0.1 1; | |
10.225.1.0/24 1; | |
} | |
map_hash_bucket_size 256; | |
map $ip_whitelist $limited_ip { | |
0 $binary_remote_addr; | |
1 ""; | |
} | |
limit_conn_zone $limited_ip zone=connsPerIP:20m; | |
limit_conn connsPerIP 30; | |
limit_conn_status 429; | |
limit_req_zone $limited_ip zone=reqsPerMinutePerIP:50m rate=500r/m; | |
limit_req zone=reqsPerMinutePerIP burst=700 nodelay; | |
limit_req_status 429; | |
client_max_body_size 64k; | |
client_header_timeout 10s; | |
client_body_timeout 10s; | |
client_body_buffer_size 16k; | |
client_header_buffer_size 4k; | |
send_timeout 10s; | |
connection_pool_size 512; | |
large_client_header_buffers 8 16k; | |
request_pool_size 4k; | |
http2_idle_timeout 60s; | |
http2_recv_timeout 10s; | |
http2_chunk_size 16k; | |
server_tokens off; | |
more_set_headers "Server: My-CDN"; | |
include /etc/nginx/mime.types; | |
variables_hash_bucket_size 128; | |
gzip on; | |
gzip_static on; # searches for the *.gz file and returns it directly from disk (compression is provided by our extra process in the background) | |
gzip_disable "msie6"; | |
gzip_min_length 4096; | |
gzip_buffers 16 64k; | |
gzip_vary on; | |
gzip_proxied any; | |
gzip_types image/svg+xml text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript text/x-component font/truetype font/opentype image/x-icon; | |
gzip_comp_level 4; | |
output_buffers 1 32k; | |
postpone_output 1460; | |
sendfile on; | |
sendfile_max_chunk 1m; | |
tcp_nopush on; | |
tcp_nodelay on; | |
keepalive_timeout 10 10; | |
ignore_invalid_headers on; | |
reset_timedout_connection on; | |
open_file_cache max=50000 inactive=30s; | |
open_file_cache_valid 10s; | |
open_file_cache_min_uses 2; | |
open_file_cache_errors on; | |
proxy_buffering on; | |
proxy_buffer_size 16k; | |
proxy_buffers 64 16k; | |
proxy_temp_path /var/lib/nginx/proxy; | |
proxy_cache_min_uses 2; | |
proxy_ignore_client_abort on; | |
proxy_intercept_errors on; | |
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; | |
proxy_redirect off; | |
proxy_connect_timeout 60; | |
proxy_send_timeout 180; | |
proxy_cache_lock on; | |
proxy_read_timeout 10s; | |
# setting up trusted IP subnets to respect X-Forwarded-For header (for multi-level proxy setup) | |
set_real_ip_from 127.0.0.1/32; | |
set_real_ip_from 10.1.2.0/24; | |
real_ip_header X-Forwarded-For; | |
real_ip_recursive on; | |
############################################################################ | |
## Example configuration for: ## | |
## https://serverip:80/myorigin.com/* -> https://www.myorigin.com/* ## | |
############################################################################ | |
upstream up_proxy_node { | |
server localhost:8000 max_conns=50; | |
keepalive 20; | |
keepalive_requests 50; | |
keepalive_timeout 5s; | |
} | |
proxy_cache_path /var/lib/nginx/tmp/proxy/global_cache levels=1:2 keys_zone=cache_up_proxy_node:20m inactive=720h max_size=10g; | |
server { | |
listen 80 default_server backlog=32768; | |
lingering_close on; | |
lingering_time 10s; | |
lingering_timeout 5s; | |
resolver 127.0.0.1; # dnsmasq with logging to get an idea of the DNS traffic that Nginx is doing | |
location ~* ^/(.+\.(css|js|jpg|jpeg|png|gif|ico))$ { | |
set $origin_uri "/$1$is_args$args"; | |
root /var/www/html; | |
access_log /var/log/nginx/ssl.access.log main buffer=4k flush=5m; | |
error_log /var/log/nginx/ssl.error.log notice; | |
if ($request_method !~ ^(GET|HEAD|OPTIONS)$ ) { | |
more_set_headers "Content-Type: application/json"; | |
return 405 '{"code": 405, "message": "Method Not Allowed"}'; | |
} | |
more_clear_headers "Strict-Transport-Security"; | |
more_set_headers "Strict-Transport-Security: max-age=31536000"; | |
more_set_headers "X-Content-Type-Options: nosniff"; | |
expires 1y; # enforce caching in browsers for 1 year (use only consciously, if you are sure that when you change the content of the file on the original, the URL will also change) | |
set $headerCorsAllowOrigin ""; | |
if ($request_method = 'OPTIONS') { | |
more_set_headers "Access-Control-Allow-Origin: $headerCorsAllowOrigin"; | |
more_set_headers "Access-Control-Allow-Methods: GET, HEAD, OPTIONS"; | |
more_set_headers "Access-Control-Max-Age: 3600"; | |
more_set_headers "Content-Length: 0"; | |
return 204; | |
} | |
set $webp ""; | |
set $file_for_webp ""; | |
if ($http_accept ~* webp) { | |
set $webp "A"; | |
} | |
if ($request_filename ~ (.+\.(png|jpe?g))$) { | |
set $file_for_webp $1; | |
} | |
if (-f $file_for_webp.webp) { | |
set $webp "${webp}E"; | |
} | |
if ($webp = AE) { | |
rewrite ^/(.+)$ /webp/$1 last; | |
} | |
proxy_cache cache_up_proxy_node; | |
proxy_cache_key "$proxy_host$request_uri"; # we don't need a schema because we only support HTTPS | |
proxy_cache_use_stale error timeout invalid_header updating http_429 http_500 http_502 http_503 http_504; | |
proxy_read_timeout 20s; | |
proxy_cache_valid 200 720h; | |
proxy_cache_valid 301 4h; | |
proxy_cache_valid 302 1h; | |
proxy_cache_valid 400 401 403 404 30s; | |
proxy_cache_valid 500 501 502 503 30s; | |
proxy_cache_valid 429 10s; | |
# due to keep-alive on origins | |
proxy_http_version 1.1; | |
proxy_set_header Connection ""; | |
proxy_set_header "Via" "My-CDN"; | |
proxy_set_header Accept-Encoding ""; # we always want to receive and cache RAW content from the origin, because we have a process for preparing static *.gz and *.br versions | |
proxy_set_header Host dotin.us; | |
proxy_set_header X-Forwarded-For $remote_addr; | |
proxy_set_header X-Forwarded-Host $host:$server_port; | |
proxy_set_header X-Forwarded-Server $host; | |
proxy_set_header X-Forwarded-Proto $scheme; | |
add_header X-Cache-Status $upstream_cache_status; | |
if (!-f $request_filename) { | |
proxy_pass http://up_proxy_node$origin_uri; | |
} | |
} | |
# internal location for webp | |
location ~* ^/webp(/(.*))$ { | |
internal; | |
root /var/www/html; | |
set $origin_uri "/$1$is_args$args"; | |
access_log /var/log/nginx/ssl.access.webp.log main buffer=4k flush=5m; | |
expires 366d; | |
more_clear_headers 'Vary'; | |
more_set_headers "Vary: Accept"; | |
more_set_headers "X-Cache: HIT"; | |
try_files $1.webp $1 =404; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment