[{"data":1,"prerenderedAt":870},["ShallowReactive",2],{"$fUFRhd37yu9e3af3b24YTx9YzFUSSCS1ZBuaZKKMH9Ms":3,"$fg2L75CwrCC6pwLNQ5vr1B-NRaAvmtc9CeEXtdUnyOeE":8},[4],{"title":5,"path":6,"language":7},"开始用 caddy 了","nginx-to-caddy","zh-cn",{"path":9,"title":5,"description":10,"body":11,"password":865,"tags":866,"date":869,"language":7},"/posts/nginx-to-caddy","这段时间 apt update 一直有一个 nginx 的 ppa 报 403 1，查了一下才发现 oerdnj 维护得心累了就删了",{"type":12,"value":13,"toc":861},"minimark",[14,43,46,49,799,802,814,817,857],[15,16,17,18,22,23,35,36,42],"p",{},"这段时间 ",[19,20,21],"code",{},"apt update"," 一直有一个 nginx 的 ppa 报 403 ",[24,25,26],"sup",{},[27,28,34],"a",{"href":29,"ariaDescribedBy":30,"dataFootnoteRef":32,"id":33},"#user-content-fn-ondrej-nginx-ppa-error",[31],"footnote-label","","user-content-fnref-ondrej-nginx-ppa-error","1","，查了一下才发现 ",[27,37,41],{"href":38,"rel":39},"https://codeberg.org/oerdnj/deb.sury.org/issues/67",[40],"nofollow","oerdnj"," 维护得心累了就删了",[15,44,45],{},"nginx 那些魔法一样的配置我一直都没太搞懂，我自己手上的这份大概是刚开始用 lnmp.org 时自动生成的（也可能是对照着 apache 配置改的，前 LLM 时代的东西记不清了），之后就在原始版本的基础上修修补补。反正这次都要换，干脆全换了的想法就自然冒出来了",[15,47,48],{},"这几年我已经把全部纯前端网站和低请求量 API 都搬到 serverless 上了，nginx 剩下工作只有反代 js 或者 go 的服务以及提供较大容量的静态资产文件访问，所以迁移也不会很复杂，跟 LLM 愉快聊天再自己翻翻文档补充就转换好了，下面放出混合了各种配置的样例；",[50,51,55],"pre",{"className":52,"code":53,"language":54,"meta":32,"style":32},"language-Caddyfile shiki shiki-themes github-light github-dark","# 我为了方便编辑 markdown 把全部 \\t 换成四空格了\n\n{\n    auto_https off\n    admin off\n}\n\nhttps://somepath.nest.moe, https://somepath2.nest.moe {\n    # 我用 Cloudflare 反代，所以要放证书，前面关自动 https 也是这原因\n    tls /abcdefg.crt /abcdefg.key\n\n    log {\n        output file /logs/abcdefg.log\n    }\n\n    # 删掉两个会暴露 caddy 的 header，虽然暴露了也没什么大不了的，但我不喜欢\n    header {\n        -Via\n        -Server\n        X-Frame-Options DENY\n        X-Content-Type-Options nosniff\n        X-Xss-Protection 1\n    }\n\n    # 用 route 主要是为了强制顺序检查\n    ## 通过这里可以看出官方 twitter monitor 后端其实是一大堆细碎的服务组装起来的，跟开源版丢一起不同\n    route {\n        handle /static/* {\n            root * /tmv3/apps/backend\n\n            header {\n                Cache-Control \"public, max-age=31536000, immutable\"\n            }\n\n            file_server\n        }\n\n        @twmedia {\n            path /media /media/*\n        }\n\n        handle @twmedia {\n            uri strip_prefix /media\n            reverse_proxy 127.0.0.1:11111\n        }\n\n        @twitterVideo {\n            path_regexp twitter ^/(amplify_video|ext_tw_video)/(.*)$\n        }\n\n        redir @twitterVideo /media/{re.twitter.1}/{re.twitter.2} 307\n\n        # 唉，v4 做到一半胎死腹中……\n        @wspush {\n            path /online/api/v4/push/*\n            header Connection *Upgrade*\n            header Upgrade    websocket\n        }\n\n        handle @wspush {\n            uri replace /online/api/v4/push /api\n            reverse_proxy 127.0.0.1:22222\n        }\n\n        handle /online/api/v4/* {\n            reverse_proxy 127.0.0.1:33333\n        }\n\n        handle {\n            reverse_proxy 127.0.0.1:44444\n        }\n    }\n}\n\nhttps://anothersite.nest.moe {\n    root * /var/www/notfun\n\n    # 这是另一个下载站的\n    @apks path *.apks\n    header @apks Content-Type application/vnd.android.package-archive\n\n    php_fastcgi unix//run/php/php8.4-fpm.sock {\n        index index.php\n        env PHP_ADMIN_VALUE \"open_basedir=/var/www/notfun:/tmp/\"\n    }\n\n    file_server\n\n    @hidden {\n        path /hidden /hidden/*\n    }\n\n    handle @hidden {\n        basicauth * {\n            user password # 这里需要自己去转换密码，读不了 .htpasses\n        }\n    }\n}\n","Caddyfile",[19,56,57,66,73,80,90,98,104,109,122,128,141,146,154,166,172,177,183,191,197,203,212,221,230,235,240,246,252,260,271,283,288,296,306,312,317,323,329,334,342,354,359,364,374,386,395,400,405,413,422,427,432,453,458,464,472,480,488,496,501,506,516,530,538,543,548,558,566,571,576,583,591,596,601,606,611,619,630,635,641,653,664,669,680,689,701,706,711,717,722,730,742,747,752,763,772,784,789,794],{"__ignoreMap":32},[58,59,62],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"sJ8bj","# 我为了方便编辑 markdown 把全部 \\t 换成四空格了\n",[58,67,69],{"class":60,"line":68},2,[58,70,72],{"emptyLinePlaceholder":71},true,"\n",[58,74,76],{"class":60,"line":75},3,[58,77,79],{"class":78},"sVt8B","{\n",[58,81,83,87],{"class":60,"line":82},4,[58,84,86],{"class":85},"sj4cs","    auto_https",[58,88,89],{"class":78}," off\n",[58,91,93,96],{"class":60,"line":92},5,[58,94,95],{"class":85},"    admin",[58,97,89],{"class":78},[58,99,101],{"class":60,"line":100},6,[58,102,103],{"class":78},"}\n",[58,105,107],{"class":60,"line":106},7,[58,108,72],{"emptyLinePlaceholder":71},[58,110,112,116,119],{"class":60,"line":111},8,[58,113,115],{"class":114},"szBVR","https://somepath.nest.moe,",[58,117,118],{"class":114}," https://somepath2.nest.moe",[58,120,121],{"class":78}," {\n",[58,123,125],{"class":60,"line":124},9,[58,126,127],{"class":64},"    # 我用 Cloudflare 反代，所以要放证书，前面关自动 https 也是这原因\n",[58,129,131,135,138],{"class":60,"line":130},10,[58,132,134],{"class":133},"sScJk","    tls",[58,136,137],{"class":114}," /abcdefg.crt",[58,139,140],{"class":114}," /abcdefg.key\n",[58,142,144],{"class":60,"line":143},11,[58,145,72],{"emptyLinePlaceholder":71},[58,147,149,152],{"class":60,"line":148},12,[58,150,151],{"class":133},"    log",[58,153,121],{"class":78},[58,155,157,160,163],{"class":60,"line":156},13,[58,158,159],{"class":133},"        output",[58,161,162],{"class":78}," file ",[58,164,165],{"class":114},"/logs/abcdefg.log\n",[58,167,169],{"class":60,"line":168},14,[58,170,171],{"class":78},"    }\n",[58,173,175],{"class":60,"line":174},15,[58,176,72],{"emptyLinePlaceholder":71},[58,178,180],{"class":60,"line":179},16,[58,181,182],{"class":64},"    # 删掉两个会暴露 caddy 的 header，虽然暴露了也没什么大不了的，但我不喜欢\n",[58,184,186,189],{"class":60,"line":185},17,[58,187,188],{"class":133},"    header",[58,190,121],{"class":78},[58,192,194],{"class":60,"line":193},18,[58,195,196],{"class":133},"        -Via\n",[58,198,200],{"class":60,"line":199},19,[58,201,202],{"class":133},"        -Server\n",[58,204,206,209],{"class":60,"line":205},20,[58,207,208],{"class":133},"        X-Frame-Options",[58,210,211],{"class":78}," DENY\n",[58,213,215,218],{"class":60,"line":214},21,[58,216,217],{"class":133},"        X-Content-Type-Options",[58,219,220],{"class":78}," nosniff\n",[58,222,224,227],{"class":60,"line":223},22,[58,225,226],{"class":133},"        X-Xss-Protection",[58,228,229],{"class":78}," 1\n",[58,231,233],{"class":60,"line":232},23,[58,234,171],{"class":78},[58,236,238],{"class":60,"line":237},24,[58,239,72],{"emptyLinePlaceholder":71},[58,241,243],{"class":60,"line":242},25,[58,244,245],{"class":64},"    # 用 route 主要是为了强制顺序检查\n",[58,247,249],{"class":60,"line":248},26,[58,250,251],{"class":64},"    ## 通过这里可以看出官方 twitter monitor 后端其实是一大堆细碎的服务组装起来的，跟开源版丢一起不同\n",[58,253,255,258],{"class":60,"line":254},27,[58,256,257],{"class":133},"    route",[58,259,121],{"class":78},[58,261,263,266,269],{"class":60,"line":262},28,[58,264,265],{"class":133},"        handle",[58,267,268],{"class":114}," /static/*",[58,270,121],{"class":78},[58,272,274,277,280],{"class":60,"line":273},29,[58,275,276],{"class":133},"            root",[58,278,279],{"class":78}," * ",[58,281,282],{"class":114},"/tmv3/apps/backend\n",[58,284,286],{"class":60,"line":285},30,[58,287,72],{"emptyLinePlaceholder":71},[58,289,291,294],{"class":60,"line":290},31,[58,292,293],{"class":133},"            header",[58,295,121],{"class":78},[58,297,299,302],{"class":60,"line":298},32,[58,300,301],{"class":133},"                Cache-Control",[58,303,305],{"class":304},"sZZnC"," \"public, max-age=31536000, immutable\"\n",[58,307,309],{"class":60,"line":308},33,[58,310,311],{"class":78},"            }\n",[58,313,315],{"class":60,"line":314},34,[58,316,72],{"emptyLinePlaceholder":71},[58,318,320],{"class":60,"line":319},35,[58,321,322],{"class":133},"            file_server\n",[58,324,326],{"class":60,"line":325},36,[58,327,328],{"class":78},"        }\n",[58,330,332],{"class":60,"line":331},37,[58,333,72],{"emptyLinePlaceholder":71},[58,335,337,340],{"class":60,"line":336},38,[58,338,339],{"class":85},"        @twmedia",[58,341,121],{"class":78},[58,343,345,348,351],{"class":60,"line":344},39,[58,346,347],{"class":133},"            path",[58,349,350],{"class":114}," /media",[58,352,353],{"class":114}," /media/*\n",[58,355,357],{"class":60,"line":356},40,[58,358,328],{"class":78},[58,360,362],{"class":60,"line":361},41,[58,363,72],{"emptyLinePlaceholder":71},[58,365,367,369,372],{"class":60,"line":366},42,[58,368,265],{"class":133},[58,370,371],{"class":85}," @twmedia",[58,373,121],{"class":78},[58,375,377,380,383],{"class":60,"line":376},43,[58,378,379],{"class":133},"            uri",[58,381,382],{"class":78}," strip_prefix ",[58,384,385],{"class":114},"/media\n",[58,387,389,392],{"class":60,"line":388},44,[58,390,391],{"class":133},"            reverse_proxy",[58,393,394],{"class":114}," 127.0.0.1:11111\n",[58,396,398],{"class":60,"line":397},45,[58,399,328],{"class":78},[58,401,403],{"class":60,"line":402},46,[58,404,72],{"emptyLinePlaceholder":71},[58,406,408,411],{"class":60,"line":407},47,[58,409,410],{"class":85},"        @twitterVideo",[58,412,121],{"class":78},[58,414,416,419],{"class":60,"line":415},48,[58,417,418],{"class":133},"            path_regexp",[58,420,421],{"class":78}," twitter ^/(amplify_video|ext_tw_video)/(.*)$\n",[58,423,425],{"class":60,"line":424},49,[58,426,328],{"class":78},[58,428,430],{"class":60,"line":429},50,[58,431,72],{"emptyLinePlaceholder":71},[58,433,435,438,441,444,447,450],{"class":60,"line":434},51,[58,436,437],{"class":133},"        redir",[58,439,440],{"class":85}," @twitterVideo",[58,442,443],{"class":114}," /media/{re.twitter.1}",[58,445,446],{"class":78},"/",[58,448,449],{"class":114},"{re.twitter.2}",[58,451,452],{"class":85}," 307\n",[58,454,456],{"class":60,"line":455},52,[58,457,72],{"emptyLinePlaceholder":71},[58,459,461],{"class":60,"line":460},53,[58,462,463],{"class":64},"        # 唉，v4 做到一半胎死腹中……\n",[58,465,467,470],{"class":60,"line":466},54,[58,468,469],{"class":85},"        @wspush",[58,471,121],{"class":78},[58,473,475,477],{"class":60,"line":474},55,[58,476,347],{"class":133},[58,478,479],{"class":114}," /online/api/v4/push/*\n",[58,481,483,485],{"class":60,"line":482},56,[58,484,293],{"class":133},[58,486,487],{"class":78}," Connection *Upgrade*\n",[58,489,491,493],{"class":60,"line":490},57,[58,492,293],{"class":133},[58,494,495],{"class":78}," Upgrade    websocket\n",[58,497,499],{"class":60,"line":498},58,[58,500,328],{"class":78},[58,502,504],{"class":60,"line":503},59,[58,505,72],{"emptyLinePlaceholder":71},[58,507,509,511,514],{"class":60,"line":508},60,[58,510,265],{"class":133},[58,512,513],{"class":85}," @wspush",[58,515,121],{"class":78},[58,517,519,521,524,527],{"class":60,"line":518},61,[58,520,379],{"class":133},[58,522,523],{"class":78}," replace ",[58,525,526],{"class":114},"/online/api/v4/push",[58,528,529],{"class":114}," /api\n",[58,531,533,535],{"class":60,"line":532},62,[58,534,391],{"class":133},[58,536,537],{"class":114}," 127.0.0.1:22222\n",[58,539,541],{"class":60,"line":540},63,[58,542,328],{"class":78},[58,544,546],{"class":60,"line":545},64,[58,547,72],{"emptyLinePlaceholder":71},[58,549,551,553,556],{"class":60,"line":550},65,[58,552,265],{"class":133},[58,554,555],{"class":114}," /online/api/v4/*",[58,557,121],{"class":78},[58,559,561,563],{"class":60,"line":560},66,[58,562,391],{"class":133},[58,564,565],{"class":114}," 127.0.0.1:33333\n",[58,567,569],{"class":60,"line":568},67,[58,570,328],{"class":78},[58,572,574],{"class":60,"line":573},68,[58,575,72],{"emptyLinePlaceholder":71},[58,577,579,581],{"class":60,"line":578},69,[58,580,265],{"class":133},[58,582,121],{"class":78},[58,584,586,588],{"class":60,"line":585},70,[58,587,391],{"class":133},[58,589,590],{"class":114}," 127.0.0.1:44444\n",[58,592,594],{"class":60,"line":593},71,[58,595,328],{"class":78},[58,597,599],{"class":60,"line":598},72,[58,600,171],{"class":78},[58,602,604],{"class":60,"line":603},73,[58,605,103],{"class":78},[58,607,609],{"class":60,"line":608},74,[58,610,72],{"emptyLinePlaceholder":71},[58,612,614,617],{"class":60,"line":613},75,[58,615,616],{"class":114},"https://anothersite.nest.moe",[58,618,121],{"class":78},[58,620,622,625,627],{"class":60,"line":621},76,[58,623,624],{"class":133},"    root",[58,626,279],{"class":78},[58,628,629],{"class":114},"/var/www/notfun\n",[58,631,633],{"class":60,"line":632},77,[58,634,72],{"emptyLinePlaceholder":71},[58,636,638],{"class":60,"line":637},78,[58,639,640],{"class":64},"    # 这是另一个下载站的\n",[58,642,644,647,650],{"class":60,"line":643},79,[58,645,646],{"class":85},"    @apks",[58,648,649],{"class":78}," path ",[58,651,652],{"class":114},"*.apks\n",[58,654,656,658,661],{"class":60,"line":655},80,[58,657,188],{"class":133},[58,659,660],{"class":85}," @apks",[58,662,663],{"class":78}," Content-Type application/vnd.android.package-archive\n",[58,665,667],{"class":60,"line":666},81,[58,668,72],{"emptyLinePlaceholder":71},[58,670,672,675,678],{"class":60,"line":671},82,[58,673,674],{"class":133},"    php_fastcgi",[58,676,677],{"class":114}," unix//run/php/php8.4-fpm.sock",[58,679,121],{"class":78},[58,681,683,686],{"class":60,"line":682},83,[58,684,685],{"class":133},"        index",[58,687,688],{"class":114}," index.php\n",[58,690,692,695,698],{"class":60,"line":691},84,[58,693,694],{"class":133},"        env",[58,696,697],{"class":78}," PHP_ADMIN_VALUE ",[58,699,700],{"class":304},"\"open_basedir=/var/www/notfun:/tmp/\"\n",[58,702,704],{"class":60,"line":703},85,[58,705,171],{"class":78},[58,707,709],{"class":60,"line":708},86,[58,710,72],{"emptyLinePlaceholder":71},[58,712,714],{"class":60,"line":713},87,[58,715,716],{"class":133},"    file_server\n",[58,718,720],{"class":60,"line":719},88,[58,721,72],{"emptyLinePlaceholder":71},[58,723,725,728],{"class":60,"line":724},89,[58,726,727],{"class":85},"    @hidden",[58,729,121],{"class":78},[58,731,733,736,739],{"class":60,"line":732},90,[58,734,735],{"class":133},"        path",[58,737,738],{"class":114}," /hidden",[58,740,741],{"class":114}," /hidden/*\n",[58,743,745],{"class":60,"line":744},91,[58,746,171],{"class":78},[58,748,750],{"class":60,"line":749},92,[58,751,72],{"emptyLinePlaceholder":71},[58,753,755,758,761],{"class":60,"line":754},93,[58,756,757],{"class":133},"    handle",[58,759,760],{"class":85}," @hidden",[58,762,121],{"class":78},[58,764,766,769],{"class":60,"line":765},94,[58,767,768],{"class":133},"        basicauth",[58,770,771],{"class":78}," * {\n",[58,773,775,778,781],{"class":60,"line":774},95,[58,776,777],{"class":133},"            user",[58,779,780],{"class":78}," password",[58,782,783],{"class":64}," # 这里需要自己去转换密码，读不了 .htpasses\n",[58,785,787],{"class":60,"line":786},96,[58,788,328],{"class":78},[58,790,792],{"class":60,"line":791},97,[58,793,171],{"class":78},[58,795,797],{"class":60,"line":796},98,[58,798,103],{"class":78},[15,800,801],{},"Caddy 的自动续签证书我用不上，因为 Cloudflare 跟 CDN 通信用的都是很多很多年有效期自签证书 XD",[15,803,804,805,813],{},"默认配置很好，很多魔法配置都可以丢了，不会响应没配置的请求，以前用 nginx 默认会响应纯 ip 的请求，然后还会带上 Cloudflare 签的证书 ",[24,806,807],{},[27,808,812],{"href":809,"ariaDescribedBy":810,"dataFootnoteRef":32,"id":811},"#user-content-fn-cf-no-filter",[31],"user-content-fnref-cf-no-filter","2","，导致 ip 暴露了相当长时间，直到我另外加了一块直接返回 444",[15,815,816],{},"目测法盯了一段时间的 top，内存占用比 nginx 高，但又不差这点，就这样吧",[818,819,822,828],"section",{"className":820,"dataFootnotes":32},[821],"footnotes",[823,824,827],"h2",{"className":825,"id":31},[826],"sr-only","注释",[829,830,831,848],"ol",{},[832,833,835,836,840,841],"li",{"id":834},"user-content-fn-ondrej-nginx-ppa-error","Err:6 ",[27,837,838],{"href":838,"rel":839},"https://ppa.launchpadcontent.net/ondrej/nginx/ubuntu",[40]," noble InRelease 403  Forbidden [IP: REDACTED 443] ",[27,842,847],{"href":843,"ariaLabel":844,"className":845,"dataFootnoteBackref":32},"#user-content-fnref-ondrej-nginx-ppa-error","Back to reference 1",[846],"data-footnote-backref","↩",[832,849,851,852],{"id":850},"user-content-fn-cf-no-filter","没限制 ip 是我的问题，但默认配置漏响应不能全怪我吧 ",[27,853,847],{"href":854,"ariaLabel":855,"className":856,"dataFootnoteBackref":32},"#user-content-fnref-cf-no-filter","Back to reference 2",[846],[858,859,860],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":32,"searchDepth":68,"depth":68,"links":862},[863],{"id":31,"depth":68,"text":864},"Footnotes",false,[867,868],"nginx","caddy","2026-05-09T07:17:00.000Z",1778320679462]