[{"data":1,"prerenderedAt":1334},["ShallowReactive",2],{"\u002Fdocs\u002Fself-hosting\u002Freverse-proxy":3},{"id":4,"title":5,"body":6,"description":1326,"extension":1327,"meta":1328,"navigation":1329,"path":1330,"seo":1331,"stem":1332,"__hash__":1333},"docs\u002Fdocs\u002Fself-hosting\u002Freverse-proxy.md","Reverse Proxy & HTTPS",{"type":7,"value":8,"toc":1319},"minimark",[9,13,22,25,71,76,131,135,1056,1068,1072,1075,1088,1173,1190,1194,1201,1257,1260,1277,1297,1301,1315],[10,11,5],"h1",{"id":12},"reverse-proxy-https",[14,15,16,17,21],"p",{},"Delivr's services speak plain HTTP on ",[18,19,20],"code",{},"localhost",". A reverse proxy in front of them terminates TLS, serves both domains, and is the only thing exposed to the internet.",[14,23,24],{},"All examples use:",[26,27,28,41],"table",{},[29,30,31],"thead",{},[32,33,34,38],"tr",{},[35,36,37],"th",{},"Domain",[35,39,40],{},"Forwards to",[42,43,44,58],"tbody",{},[32,45,46,52],{},[47,48,49],"td",{},[18,50,51],{},"mail.example.com",[47,53,54,55],{},"Delivr Web — ",[18,56,57],{},"127.0.0.1:14128",[32,59,60,65],{},[47,61,62],{},[18,63,64],{},"api.mail.example.com",[47,66,67,68],{},"Delivr API — ",[18,69,70],{},"127.0.0.1:14123",[72,73,75],"h2",{"id":74},"what-the-proxy-must-do","What the proxy must do",[77,78,79,87,101,107,121],"ul",{},[80,81,82,86],"li",{},[83,84,85],"strong",{},"Serve HTTPS."," Browsers only install PWAs and keep secure cookies on HTTPS origins.",[80,88,89,92,93,96,97,100],{},[83,90,91],{},"Allow large request bodies on the API."," Sending a mail with attachments can be up to ",[18,94,95],{},"DLA_MAX_ATTACHMENT_SIZE_MB"," + 16 MB — ",[83,98,99],{},"41 MB"," with the defaults. Many proxies default to 1 MB.",[80,102,103,106],{},[83,104,105],{},"Allow slow responses."," Some IMAP operations (large folders, big attachments, slow mail servers) take longer than typical web requests. A read timeout of 120 seconds is a safe choice.",[80,108,109,112,113,116,117,120],{},[83,110,111],{},"Forward the client address."," Login rate limiting needs to tell clients apart. All examples below pass it on in ",[18,114,115],{},"X-Forwarded-For"," — set ",[18,118,119],{},"DLA_TRUST_PROXY=true"," on the API so Delivr uses it.",[80,122,123,126,127,130],{},[83,124,125],{},"Not cache API responses."," Delivr sends ",[18,128,129],{},"Cache-Control: no-store"," for attachments; don't override it.",[72,132,134],{"id":133},"configurations","Configurations",[136,137,138,237,611,849],"tabs",{},[139,140,143,146,230],"tabs-item",{"icon":141,"label":142},"i-lucide-shield-check","Caddy",[14,144,145],{},"Caddy obtains and renews certificates automatically — this is the shortest working setup.",[147,148,154],"pre",{"className":149,"code":150,"filename":151,"language":152,"meta":153,"style":153},"language-caddy shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","mail.example.com {\n    encode zstd gzip\n    reverse_proxy 127.0.0.1:14128\n}\n\napi.mail.example.com {\n    request_body {\n        max_size 50MB\n    }\n    # Caddy has no proxy timeout by default, so slow IMAP operations are fine.\n    reverse_proxy 127.0.0.1:14123\n}\n","\u002Fetc\u002Fcaddy\u002FCaddyfile","caddy","",[18,155,156,164,170,176,182,189,195,201,207,213,219,225],{"__ignoreMap":153},[157,158,161],"span",{"class":159,"line":160},"line",1,[157,162,163],{},"mail.example.com {\n",[157,165,167],{"class":159,"line":166},2,[157,168,169],{},"    encode zstd gzip\n",[157,171,173],{"class":159,"line":172},3,[157,174,175],{},"    reverse_proxy 127.0.0.1:14128\n",[157,177,179],{"class":159,"line":178},4,[157,180,181],{},"}\n",[157,183,185],{"class":159,"line":184},5,[157,186,188],{"emptyLinePlaceholder":187},true,"\n",[157,190,192],{"class":159,"line":191},6,[157,193,194],{},"api.mail.example.com {\n",[157,196,198],{"class":159,"line":197},7,[157,199,200],{},"    request_body {\n",[157,202,204],{"class":159,"line":203},8,[157,205,206],{},"        max_size 50MB\n",[157,208,210],{"class":159,"line":209},9,[157,211,212],{},"    }\n",[157,214,216],{"class":159,"line":215},10,[157,217,218],{},"    # Caddy has no proxy timeout by default, so slow IMAP operations are fine.\n",[157,220,222],{"class":159,"line":221},11,[157,223,224],{},"    reverse_proxy 127.0.0.1:14123\n",[157,226,228],{"class":159,"line":227},12,[157,229,181],{},[14,231,232,233,236],{},"Reload with ",[18,234,235],{},"sudo systemctl reload caddy",".",[139,238,241,244,278,561,564],{"icon":239,"label":240},"i-lucide-server","Nginx",[14,242,243],{},"Obtain a certificate for both names first, for example with Certbot:",[147,245,249],{"className":246,"code":247,"language":248,"meta":153,"style":153},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","sudo certbot certonly --nginx -d mail.example.com -d api.mail.example.com\n","bash",[18,250,251],{"__ignoreMap":153},[157,252,253,257,261,264,267,270,273,275],{"class":159,"line":160},[157,254,256],{"class":255},"sBMFI","sudo",[157,258,260],{"class":259},"sfazB"," certbot",[157,262,263],{"class":259}," certonly",[157,265,266],{"class":259}," --nginx",[157,268,269],{"class":259}," -d",[157,271,272],{"class":259}," mail.example.com",[157,274,269],{"class":259},[157,276,277],{"class":259}," api.mail.example.com\n",[147,279,284],{"className":280,"code":281,"filename":282,"language":283,"meta":153,"style":153},"language-nginx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Redirect HTTP to HTTPS\nserver {\n    listen 80;\n    listen [::]:80;\n    server_name mail.example.com api.mail.example.com;\n    return 301 https:\u002F\u002F$host$request_uri;\n}\n\n# Web client\nserver {\n    listen 443 ssl;\n    listen [::]:443 ssl;\n    http2 on;\n    server_name mail.example.com;\n\n    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Fprivkey.pem;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:14128;\n        proxy_http_version 1.1;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n\n# API\nserver {\n    listen 443 ssl;\n    listen [::]:443 ssl;\n    http2 on;\n    server_name api.mail.example.com;\n\n    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Fprivkey.pem;\n\n    # Must be larger than DLA_MAX_ATTACHMENT_SIZE_MB + 16 MB\n    client_max_body_size 50m;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:14123;\n        proxy_http_version 1.1;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_read_timeout 120s;\n        proxy_send_timeout 120s;\n    }\n}\n","\u002Fetc\u002Fnginx\u002Fsites-available\u002Fdelivr.conf","nginx",[18,285,286,291,296,301,306,311,316,320,324,329,333,338,343,349,355,360,366,372,377,383,389,395,401,407,413,419,424,429,434,440,445,450,455,460,466,471,476,481,486,492,498,503,508,514,519,524,529,534,539,545,551,556],{"__ignoreMap":153},[157,287,288],{"class":159,"line":160},[157,289,290],{},"# Redirect HTTP to HTTPS\n",[157,292,293],{"class":159,"line":166},[157,294,295],{},"server {\n",[157,297,298],{"class":159,"line":172},[157,299,300],{},"    listen 80;\n",[157,302,303],{"class":159,"line":178},[157,304,305],{},"    listen [::]:80;\n",[157,307,308],{"class":159,"line":184},[157,309,310],{},"    server_name mail.example.com api.mail.example.com;\n",[157,312,313],{"class":159,"line":191},[157,314,315],{},"    return 301 https:\u002F\u002F$host$request_uri;\n",[157,317,318],{"class":159,"line":197},[157,319,181],{},[157,321,322],{"class":159,"line":203},[157,323,188],{"emptyLinePlaceholder":187},[157,325,326],{"class":159,"line":209},[157,327,328],{},"# Web client\n",[157,330,331],{"class":159,"line":215},[157,332,295],{},[157,334,335],{"class":159,"line":221},[157,336,337],{},"    listen 443 ssl;\n",[157,339,340],{"class":159,"line":227},[157,341,342],{},"    listen [::]:443 ssl;\n",[157,344,346],{"class":159,"line":345},13,[157,347,348],{},"    http2 on;\n",[157,350,352],{"class":159,"line":351},14,[157,353,354],{},"    server_name mail.example.com;\n",[157,356,358],{"class":159,"line":357},15,[157,359,188],{"emptyLinePlaceholder":187},[157,361,363],{"class":159,"line":362},16,[157,364,365],{},"    ssl_certificate     \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Ffullchain.pem;\n",[157,367,369],{"class":159,"line":368},17,[157,370,371],{},"    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Fprivkey.pem;\n",[157,373,375],{"class":159,"line":374},18,[157,376,188],{"emptyLinePlaceholder":187},[157,378,380],{"class":159,"line":379},19,[157,381,382],{},"    location \u002F {\n",[157,384,386],{"class":159,"line":385},20,[157,387,388],{},"        proxy_pass http:\u002F\u002F127.0.0.1:14128;\n",[157,390,392],{"class":159,"line":391},21,[157,393,394],{},"        proxy_http_version 1.1;\n",[157,396,398],{"class":159,"line":397},22,[157,399,400],{},"        proxy_set_header Host $host;\n",[157,402,404],{"class":159,"line":403},23,[157,405,406],{},"        proxy_set_header X-Real-IP $remote_addr;\n",[157,408,410],{"class":159,"line":409},24,[157,411,412],{},"        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n",[157,414,416],{"class":159,"line":415},25,[157,417,418],{},"        proxy_set_header X-Forwarded-Proto $scheme;\n",[157,420,422],{"class":159,"line":421},26,[157,423,212],{},[157,425,427],{"class":159,"line":426},27,[157,428,181],{},[157,430,432],{"class":159,"line":431},28,[157,433,188],{"emptyLinePlaceholder":187},[157,435,437],{"class":159,"line":436},29,[157,438,439],{},"# API\n",[157,441,443],{"class":159,"line":442},30,[157,444,295],{},[157,446,448],{"class":159,"line":447},31,[157,449,337],{},[157,451,453],{"class":159,"line":452},32,[157,454,342],{},[157,456,458],{"class":159,"line":457},33,[157,459,348],{},[157,461,463],{"class":159,"line":462},34,[157,464,465],{},"    server_name api.mail.example.com;\n",[157,467,469],{"class":159,"line":468},35,[157,470,188],{"emptyLinePlaceholder":187},[157,472,474],{"class":159,"line":473},36,[157,475,365],{},[157,477,479],{"class":159,"line":478},37,[157,480,371],{},[157,482,484],{"class":159,"line":483},38,[157,485,188],{"emptyLinePlaceholder":187},[157,487,489],{"class":159,"line":488},39,[157,490,491],{},"    # Must be larger than DLA_MAX_ATTACHMENT_SIZE_MB + 16 MB\n",[157,493,495],{"class":159,"line":494},40,[157,496,497],{},"    client_max_body_size 50m;\n",[157,499,501],{"class":159,"line":500},41,[157,502,188],{"emptyLinePlaceholder":187},[157,504,506],{"class":159,"line":505},42,[157,507,382],{},[157,509,511],{"class":159,"line":510},43,[157,512,513],{},"        proxy_pass http:\u002F\u002F127.0.0.1:14123;\n",[157,515,517],{"class":159,"line":516},44,[157,518,394],{},[157,520,522],{"class":159,"line":521},45,[157,523,400],{},[157,525,527],{"class":159,"line":526},46,[157,528,406],{},[157,530,532],{"class":159,"line":531},47,[157,533,412],{},[157,535,537],{"class":159,"line":536},48,[157,538,418],{},[157,540,542],{"class":159,"line":541},49,[157,543,544],{},"        proxy_read_timeout 120s;\n",[157,546,548],{"class":159,"line":547},50,[157,549,550],{},"        proxy_send_timeout 120s;\n",[157,552,554],{"class":159,"line":553},51,[157,555,212],{},[157,557,559],{"class":159,"line":558},52,[157,560,181],{},[14,562,563],{},"Enable it and reload:",[147,565,567],{"className":246,"code":566,"language":248,"meta":153,"style":153},"sudo ln -s \u002Fetc\u002Fnginx\u002Fsites-available\u002Fdelivr.conf \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F\nsudo nginx -t && sudo systemctl reload nginx\n",[18,568,569,585],{"__ignoreMap":153},[157,570,571,573,576,579,582],{"class":159,"line":160},[157,572,256],{"class":255},[157,574,575],{"class":259}," ln",[157,577,578],{"class":259}," -s",[157,580,581],{"class":259}," \u002Fetc\u002Fnginx\u002Fsites-available\u002Fdelivr.conf",[157,583,584],{"class":259}," \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F\n",[157,586,587,589,592,595,599,602,605,608],{"class":159,"line":166},[157,588,256],{"class":255},[157,590,591],{"class":259}," nginx",[157,593,594],{"class":259}," -t",[157,596,598],{"class":597},"sMK4o"," &&",[157,600,601],{"class":255}," sudo",[157,603,604],{"class":259}," systemctl",[157,606,607],{"class":259}," reload",[157,609,610],{"class":259}," nginx\n",[139,612,615,629,834],{"icon":613,"label":614},"i-lucide-route","Traefik",[14,616,617,618,621,622,625,626,236],{},"With Traefik running in Docker, configure Delivr through container labels. This assumes Traefik has an entrypoint called ",[18,619,620],{},"websecure",", a certificate resolver called ",[18,623,624],{},"letsencrypt",", and a shared external network called ",[18,627,628],{},"proxy",[147,630,635],{"className":631,"code":632,"filename":633,"language":634,"meta":153,"style":153},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","services:\n  delivr-api:\n    image: ghcr.io\u002Fdelivr-project\u002Fdelivr-api:latest\n    # … environment and volumes as in the Docker guide, without `ports:` …\n    networks: [proxy]\n    labels:\n      - traefik.enable=true\n      - traefik.http.routers.delivr-api.rule=Host(`api.mail.example.com`)\n      - traefik.http.routers.delivr-api.entrypoints=websecure\n      - traefik.http.routers.delivr-api.tls.certresolver=letsencrypt\n      - traefik.http.services.delivr-api.loadbalancer.server.port=14123\n\n  delivr-web:\n    image: ghcr.io\u002Fdelivr-project\u002Fdelivr-web:latest\n    # … environment as in the Docker guide, without `ports:` …\n    networks: [proxy]\n    labels:\n      - traefik.enable=true\n      - traefik.http.routers.delivr-web.rule=Host(`mail.example.com`)\n      - traefik.http.routers.delivr-web.entrypoints=websecure\n      - traefik.http.routers.delivr-web.tls.certresolver=letsencrypt\n      - traefik.http.services.delivr-web.loadbalancer.server.port=14128\n\nnetworks:\n  proxy:\n    external: true\n","docker-compose.yml","yaml",[18,636,637,646,653,664,670,685,692,700,707,714,721,728,732,739,748,753,765,771,777,784,791,798,805,809,816,823],{"__ignoreMap":153},[157,638,639,643],{"class":159,"line":160},[157,640,642],{"class":641},"swJcz","services",[157,644,645],{"class":597},":\n",[157,647,648,651],{"class":159,"line":166},[157,649,650],{"class":641},"  delivr-api",[157,652,645],{"class":597},[157,654,655,658,661],{"class":159,"line":172},[157,656,657],{"class":641},"    image",[157,659,660],{"class":597},":",[157,662,663],{"class":259}," ghcr.io\u002Fdelivr-project\u002Fdelivr-api:latest\n",[157,665,666],{"class":159,"line":178},[157,667,669],{"class":668},"sHwdD","    # … environment and volumes as in the Docker guide, without `ports:` …\n",[157,671,672,675,677,680,682],{"class":159,"line":184},[157,673,674],{"class":641},"    networks",[157,676,660],{"class":597},[157,678,679],{"class":597}," [",[157,681,628],{"class":259},[157,683,684],{"class":597},"]\n",[157,686,687,690],{"class":159,"line":191},[157,688,689],{"class":641},"    labels",[157,691,645],{"class":597},[157,693,694,697],{"class":159,"line":197},[157,695,696],{"class":597},"      -",[157,698,699],{"class":259}," traefik.enable=true\n",[157,701,702,704],{"class":159,"line":203},[157,703,696],{"class":597},[157,705,706],{"class":259}," traefik.http.routers.delivr-api.rule=Host(`api.mail.example.com`)\n",[157,708,709,711],{"class":159,"line":209},[157,710,696],{"class":597},[157,712,713],{"class":259}," traefik.http.routers.delivr-api.entrypoints=websecure\n",[157,715,716,718],{"class":159,"line":215},[157,717,696],{"class":597},[157,719,720],{"class":259}," traefik.http.routers.delivr-api.tls.certresolver=letsencrypt\n",[157,722,723,725],{"class":159,"line":221},[157,724,696],{"class":597},[157,726,727],{"class":259}," traefik.http.services.delivr-api.loadbalancer.server.port=14123\n",[157,729,730],{"class":159,"line":227},[157,731,188],{"emptyLinePlaceholder":187},[157,733,734,737],{"class":159,"line":345},[157,735,736],{"class":641},"  delivr-web",[157,738,645],{"class":597},[157,740,741,743,745],{"class":159,"line":351},[157,742,657],{"class":641},[157,744,660],{"class":597},[157,746,747],{"class":259}," ghcr.io\u002Fdelivr-project\u002Fdelivr-web:latest\n",[157,749,750],{"class":159,"line":357},[157,751,752],{"class":668},"    # … environment as in the Docker guide, without `ports:` …\n",[157,754,755,757,759,761,763],{"class":159,"line":362},[157,756,674],{"class":641},[157,758,660],{"class":597},[157,760,679],{"class":597},[157,762,628],{"class":259},[157,764,684],{"class":597},[157,766,767,769],{"class":159,"line":368},[157,768,689],{"class":641},[157,770,645],{"class":597},[157,772,773,775],{"class":159,"line":374},[157,774,696],{"class":597},[157,776,699],{"class":259},[157,778,779,781],{"class":159,"line":379},[157,780,696],{"class":597},[157,782,783],{"class":259}," traefik.http.routers.delivr-web.rule=Host(`mail.example.com`)\n",[157,785,786,788],{"class":159,"line":385},[157,787,696],{"class":597},[157,789,790],{"class":259}," traefik.http.routers.delivr-web.entrypoints=websecure\n",[157,792,793,795],{"class":159,"line":391},[157,794,696],{"class":597},[157,796,797],{"class":259}," traefik.http.routers.delivr-web.tls.certresolver=letsencrypt\n",[157,799,800,802],{"class":159,"line":397},[157,801,696],{"class":597},[157,803,804],{"class":259}," traefik.http.services.delivr-web.loadbalancer.server.port=14128\n",[157,806,807],{"class":159,"line":403},[157,808,188],{"emptyLinePlaceholder":187},[157,810,811,814],{"class":159,"line":409},[157,812,813],{"class":641},"networks",[157,815,645],{"class":597},[157,817,818,821],{"class":159,"line":415},[157,819,820],{"class":641},"  proxy",[157,822,645],{"class":597},[157,824,825,828,830],{"class":159,"line":421},[157,826,827],{"class":641},"    external",[157,829,660],{"class":597},[157,831,833],{"class":832},"sfNiH"," true\n",[14,835,836,837,840,841,844,845,848],{},"Traefik doesn't limit request bodies by default. If you use a ",[18,838,839],{},"buffering"," middleware, set ",[18,842,843],{},"maxRequestBodyBytes"," to at least ",[18,846,847],{},"52428800"," (50 MB).",[139,850,853,856,880,1020],{"icon":851,"label":852},"i-lucide-feather","Apache",[14,854,855],{},"Enable the required modules first:",[147,857,859],{"className":246,"code":858,"language":248,"meta":153,"style":153},"sudo a2enmod proxy proxy_http ssl headers\n",[18,860,861],{"__ignoreMap":153},[157,862,863,865,868,871,874,877],{"class":159,"line":160},[157,864,256],{"class":255},[157,866,867],{"class":259}," a2enmod",[157,869,870],{"class":259}," proxy",[157,872,873],{"class":259}," proxy_http",[157,875,876],{"class":259}," ssl",[157,878,879],{"class":259}," headers\n",[147,881,886],{"className":882,"code":883,"filename":884,"language":885,"meta":153,"style":153},"language-apache shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CVirtualHost *:443>\n    ServerName mail.example.com\n\n    SSLEngine on\n    SSLCertificateFile    \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Ffullchain.pem\n    SSLCertificateKeyFile \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Fprivkey.pem\n\n    ProxyPreserveHost On\n    RequestHeader set X-Forwarded-Proto \"https\"\n    ProxyPass        \u002F http:\u002F\u002F127.0.0.1:14128\u002F\n    ProxyPassReverse \u002F http:\u002F\u002F127.0.0.1:14128\u002F\n\u003C\u002FVirtualHost>\n\n\u003CVirtualHost *:443>\n    ServerName api.mail.example.com\n\n    SSLEngine on\n    SSLCertificateFile    \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Ffullchain.pem\n    SSLCertificateKeyFile \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Fprivkey.pem\n\n    # 50 MB — must be larger than DLA_MAX_ATTACHMENT_SIZE_MB + 16 MB\n    LimitRequestBody 52428800\n    ProxyTimeout 120\n\n    ProxyPreserveHost On\n    RequestHeader set X-Forwarded-Proto \"https\"\n    ProxyPass        \u002F http:\u002F\u002F127.0.0.1:14123\u002F\n    ProxyPassReverse \u002F http:\u002F\u002F127.0.0.1:14123\u002F\n\u003C\u002FVirtualHost>\n","\u002Fetc\u002Fapache2\u002Fsites-available\u002Fdelivr.conf","apache",[18,887,888,893,898,902,907,912,917,921,926,931,936,941,946,950,954,959,963,967,971,975,979,984,989,994,998,1002,1006,1011,1016],{"__ignoreMap":153},[157,889,890],{"class":159,"line":160},[157,891,892],{},"\u003CVirtualHost *:443>\n",[157,894,895],{"class":159,"line":166},[157,896,897],{},"    ServerName mail.example.com\n",[157,899,900],{"class":159,"line":172},[157,901,188],{"emptyLinePlaceholder":187},[157,903,904],{"class":159,"line":178},[157,905,906],{},"    SSLEngine on\n",[157,908,909],{"class":159,"line":184},[157,910,911],{},"    SSLCertificateFile    \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Ffullchain.pem\n",[157,913,914],{"class":159,"line":191},[157,915,916],{},"    SSLCertificateKeyFile \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmail.example.com\u002Fprivkey.pem\n",[157,918,919],{"class":159,"line":197},[157,920,188],{"emptyLinePlaceholder":187},[157,922,923],{"class":159,"line":203},[157,924,925],{},"    ProxyPreserveHost On\n",[157,927,928],{"class":159,"line":209},[157,929,930],{},"    RequestHeader set X-Forwarded-Proto \"https\"\n",[157,932,933],{"class":159,"line":215},[157,934,935],{},"    ProxyPass        \u002F http:\u002F\u002F127.0.0.1:14128\u002F\n",[157,937,938],{"class":159,"line":221},[157,939,940],{},"    ProxyPassReverse \u002F http:\u002F\u002F127.0.0.1:14128\u002F\n",[157,942,943],{"class":159,"line":227},[157,944,945],{},"\u003C\u002FVirtualHost>\n",[157,947,948],{"class":159,"line":345},[157,949,188],{"emptyLinePlaceholder":187},[157,951,952],{"class":159,"line":351},[157,953,892],{},[157,955,956],{"class":159,"line":357},[157,957,958],{},"    ServerName api.mail.example.com\n",[157,960,961],{"class":159,"line":362},[157,962,188],{"emptyLinePlaceholder":187},[157,964,965],{"class":159,"line":368},[157,966,906],{},[157,968,969],{"class":159,"line":374},[157,970,911],{},[157,972,973],{"class":159,"line":379},[157,974,916],{},[157,976,977],{"class":159,"line":385},[157,978,188],{"emptyLinePlaceholder":187},[157,980,981],{"class":159,"line":391},[157,982,983],{},"    # 50 MB — must be larger than DLA_MAX_ATTACHMENT_SIZE_MB + 16 MB\n",[157,985,986],{"class":159,"line":397},[157,987,988],{},"    LimitRequestBody 52428800\n",[157,990,991],{"class":159,"line":403},[157,992,993],{},"    ProxyTimeout 120\n",[157,995,996],{"class":159,"line":409},[157,997,188],{"emptyLinePlaceholder":187},[157,999,1000],{"class":159,"line":415},[157,1001,925],{},[157,1003,1004],{"class":159,"line":421},[157,1005,930],{},[157,1007,1008],{"class":159,"line":426},[157,1009,1010],{},"    ProxyPass        \u002F http:\u002F\u002F127.0.0.1:14123\u002F\n",[157,1012,1013],{"class":159,"line":431},[157,1014,1015],{},"    ProxyPassReverse \u002F http:\u002F\u002F127.0.0.1:14123\u002F\n",[157,1017,1018],{"class":159,"line":436},[157,1019,945],{},[147,1021,1023],{"className":246,"code":1022,"language":248,"meta":153,"style":153},"sudo a2ensite delivr && sudo apachectl configtest && sudo systemctl reload apache2\n",[18,1024,1025],{"__ignoreMap":153},[157,1026,1027,1029,1032,1035,1037,1039,1042,1045,1047,1049,1051,1053],{"class":159,"line":160},[157,1028,256],{"class":255},[157,1030,1031],{"class":259}," a2ensite",[157,1033,1034],{"class":259}," delivr",[157,1036,598],{"class":597},[157,1038,601],{"class":255},[157,1040,1041],{"class":259}," apachectl",[157,1043,1044],{"class":259}," configtest",[157,1046,598],{"class":597},[157,1048,601],{"class":255},[157,1050,604],{"class":259},[157,1052,607],{"class":259},[157,1054,1055],{"class":259}," apache2\n",[1057,1058,1059],"tip",{},[14,1060,1061,1062,1064,1065,236],{},"If you raise ",[18,1063,95],{},", raise the proxy's body limit too. The rule of thumb: ",[83,1066,1067],{},"proxy limit ≥ attachment limit + 16 MB",[72,1069,1071],{"id":1070},"verify-the-setup","Verify the setup",[14,1073,1074],{},"Check that the API answers over HTTPS:",[147,1076,1078],{"className":246,"code":1077,"language":248,"meta":153,"style":153},"curl https:\u002F\u002Fapi.mail.example.com\u002Fhealth\n",[18,1079,1080],{"__ignoreMap":153},[157,1081,1082,1085],{"class":159,"line":160},[157,1083,1084],{"class":255},"curl",[157,1086,1087],{"class":259}," https:\u002F\u002Fapi.mail.example.com\u002Fhealth\n",[147,1089,1093],{"className":1090,"code":1091,"language":1092,"meta":153,"style":153},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"success\": true,\n  \"code\": 200,\n  \"message\": \"Delivr API is running\",\n  \"data\": null\n}\n","json",[18,1094,1095,1100,1117,1134,1155,1169],{"__ignoreMap":153},[157,1096,1097],{"class":159,"line":160},[157,1098,1099],{"class":597},"{\n",[157,1101,1102,1105,1109,1112,1114],{"class":159,"line":166},[157,1103,1104],{"class":597},"  \"",[157,1106,1108],{"class":1107},"spNyl","success",[157,1110,1111],{"class":597},"\"",[157,1113,660],{"class":597},[157,1115,1116],{"class":597}," true,\n",[157,1118,1119,1121,1123,1125,1127,1131],{"class":159,"line":172},[157,1120,1104],{"class":597},[157,1122,18],{"class":1107},[157,1124,1111],{"class":597},[157,1126,660],{"class":597},[157,1128,1130],{"class":1129},"sbssI"," 200",[157,1132,1133],{"class":597},",\n",[157,1135,1136,1138,1141,1143,1145,1148,1151,1153],{"class":159,"line":178},[157,1137,1104],{"class":597},[157,1139,1140],{"class":1107},"message",[157,1142,1111],{"class":597},[157,1144,660],{"class":597},[157,1146,1147],{"class":597}," \"",[157,1149,1150],{"class":259},"Delivr API is running",[157,1152,1111],{"class":597},[157,1154,1133],{"class":597},[157,1156,1157,1159,1162,1164,1166],{"class":159,"line":184},[157,1158,1104],{"class":597},[157,1160,1161],{"class":1107},"data",[157,1163,1111],{"class":597},[157,1165,660],{"class":597},[157,1167,1168],{"class":597}," null\n",[157,1170,1171],{"class":159,"line":191},[157,1172,181],{"class":597},[14,1174,1175,1176,1179,1180,1185,1186,1189],{},"Then open ",[18,1177,1178],{},"https:\u002F\u002Fmail.example.com",". You should see the Delivr sign-in page. If the page loads but signing in fails, jump to ",[1181,1182,1184],"a",{"href":1183},"\u002Fdocs\u002Fself-hosting\u002Ftroubleshooting#sign-in-fails-or-cors-errors","Troubleshooting"," — it's almost always a mismatch between ",[18,1187,1188],{},"DLA_APP_URL"," and the web client's URL.",[72,1191,1193],{"id":1192},"single-domain-advanced","Single domain (advanced)",[14,1195,1196,1197,1200],{},"If you can only use one domain, you can serve the API under a path such as ",[18,1198,1199],{},"\u002Fapi"," and strip that prefix in the proxy. The browser then talks to the same origin, so no cross-origin requests are involved.",[147,1202,1204],{"className":149,"code":1203,"filename":151,"language":152,"meta":153,"style":153},"mail.example.com {\n    handle_path \u002Fapi\u002F* {\n        request_body {\n            max_size 50MB\n        }\n        reverse_proxy 127.0.0.1:14123\n    }\n    handle {\n        reverse_proxy 127.0.0.1:14128\n    }\n}\n",[18,1205,1206,1210,1215,1220,1225,1230,1235,1239,1244,1249,1253],{"__ignoreMap":153},[157,1207,1208],{"class":159,"line":160},[157,1209,163],{},[157,1211,1212],{"class":159,"line":166},[157,1213,1214],{},"    handle_path \u002Fapi\u002F* {\n",[157,1216,1217],{"class":159,"line":172},[157,1218,1219],{},"        request_body {\n",[157,1221,1222],{"class":159,"line":178},[157,1223,1224],{},"            max_size 50MB\n",[157,1226,1227],{"class":159,"line":184},[157,1228,1229],{},"        }\n",[157,1231,1232],{"class":159,"line":191},[157,1233,1234],{},"        reverse_proxy 127.0.0.1:14123\n",[157,1236,1237],{"class":159,"line":197},[157,1238,212],{},[157,1240,1241],{"class":159,"line":203},[157,1242,1243],{},"    handle {\n",[157,1245,1246],{"class":159,"line":209},[157,1247,1248],{},"        reverse_proxy 127.0.0.1:14128\n",[157,1250,1251],{"class":159,"line":215},[157,1252,212],{},[157,1254,1255],{"class":159,"line":221},[157,1256,181],{},[14,1258,1259],{},"Then configure:",[77,1261,1262,1267,1272],{},[80,1263,1264],{},[18,1265,1266],{},"NUXT_PUBLIC_API_URL=https:\u002F\u002Fmail.example.com\u002Fapi\u002Fv1",[80,1268,1269],{},[18,1270,1271],{},"NUXT_PUBLIC_APP_URL=https:\u002F\u002Fmail.example.com",[80,1273,1274],{},[18,1275,1276],{},"DLA_APP_URL=https:\u002F\u002Fmail.example.com",[1278,1279,1280],"caution",{},[14,1281,1282,1285,1286,1288,1289,1292,1293,1296],{},[18,1283,1284],{},"handle_path"," removes the ",[18,1287,1199],{}," prefix before forwarding, so the API still receives ",[18,1290,1291],{},"\u002Fv1\u002F…",". Don't forward the prefix — the API doesn't know about it. The interactive API reference also expects to live at the root of its domain, so with this layout it won't load correctly; set ",[18,1294,1295],{},"DLA_DISABLE_DOCS=true"," or use a separate API domain if you need it.",[72,1298,1300],{"id":1299},"next-steps","Next steps",[77,1302,1303,1309],{},[80,1304,1305],{},[1181,1306,1308],{"href":1307},"\u002Fdocs\u002Fself-hosting\u002Ffirst-run","First Run & Admin Setup",[80,1310,1311],{},[1181,1312,1314],{"href":1313},"\u002Fdocs\u002Fself-hosting\u002Fhardening","Production Hardening",[1316,1317,1318],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":153,"searchDepth":166,"depth":166,"links":1320},[1321,1322,1323,1324,1325],{"id":74,"depth":166,"text":75},{"id":133,"depth":166,"text":134},{"id":1070,"depth":166,"text":1071},{"id":1192,"depth":166,"text":1193},{"id":1299,"depth":166,"text":1300},"Serve Delivr over HTTPS with Caddy, Nginx, Traefik, or Apache. Includes body-size limits, timeouts, and a single-domain setup.","md",{},{"title":5},"\u002Fdocs\u002Fself-hosting\u002Freverse-proxy",{"title":5,"description":1326},"docs\u002Fself-hosting\u002Freverse-proxy","TmKRoKwwr32vc27dX7zDWv2VuEYBD8VpL6LoCdt5J3A",1791069426920]