[{"data":1,"prerenderedAt":1550},["ShallowReactive",2],{"\u002Ffix-issues\u002Fflask-404-on-static-or-media-files":3},{"id":4,"title":5,"body":6,"description":1540,"extension":1541,"meta":1542,"navigation":71,"path":1546,"seo":1547,"stem":1548,"__hash__":1549},"content\u002Ffix-issues\u002Fflask-404-on-static-or-media-files.md","Flask 404 on Static or Media Files",{"type":7,"value":8,"toc":1529},"minimark",[9,13,17,22,338,345,349,360,364,1118,1122,1221,1225,1228,1273,1276,1331,1334,1363,1367,1446,1450,1477,1481,1489,1497,1505,1513,1517,1525],[10,11,5],"h1",{"id":12},"flask-404-on-static-or-media-files",[14,15,16],"p",{},"If you're getting 404 errors for CSS, JavaScript, images, or uploaded files in production, this guide shows you how to trace the request path and fix the serving configuration step-by-step. The goal is to make static and media URLs resolve correctly through Nginx and Flask.",[18,19,21],"h2",{"id":20},"quick-fix-quick-setup","Quick Fix \u002F Quick Setup",[23,24,29],"pre",{"className":25,"code":26,"language":27,"meta":28,"style":28},"language-bash shiki shiki-themes github-light github-dark","# 1) Confirm the requested URL path matches your Nginx config\ncurl -I https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\ncurl -I https:\u002F\u002Fyour-domain.com\u002Fmedia\u002Fuploads\u002Fexample.jpg\n\n# 2) Check Nginx location blocks\nsudo nginx -T | sed -n '\u002Fserver_name your-domain.com\u002F,\u002F}\u002Fp'\n\n# Example working config\nserver {\n    server_name your-domain.com;\n\n    location \u002Fstatic\u002F {\n        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F;\n        expires 30d;\n        access_log off;\n    }\n\n    location \u002Fmedia\u002F {\n        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F;\n    }\n\n    location \u002F {\n        include proxy_params;\n        proxy_pass http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock;\n    }\n}\n\n# 3) Verify files actually exist on disk\nls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F\nls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F\n\n# 4) Test and reload Nginx\nsudo nginx -t && sudo systemctl reload nginx\n","bash","",[30,31,32,41,56,66,73,79,104,109,115,124,137,142,153,164,175,186,192,197,207,217,222,227,237,248,259,264,270,275,281,293,303,308,314],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sJ8bj","# 1) Confirm the requested URL path matches your Nginx config\n",[33,42,44,48,52],{"class":35,"line":43},2,[33,45,47],{"class":46},"sScJk","curl",[33,49,51],{"class":50},"sj4cs"," -I",[33,53,55],{"class":54},"sZZnC"," https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\n",[33,57,59,61,63],{"class":35,"line":58},3,[33,60,47],{"class":46},[33,62,51],{"class":50},[33,64,65],{"class":54}," https:\u002F\u002Fyour-domain.com\u002Fmedia\u002Fuploads\u002Fexample.jpg\n",[33,67,69],{"class":35,"line":68},4,[33,70,72],{"emptyLinePlaceholder":71},true,"\n",[33,74,76],{"class":35,"line":75},5,[33,77,78],{"class":39},"# 2) Check Nginx location blocks\n",[33,80,82,85,88,91,95,98,101],{"class":35,"line":81},6,[33,83,84],{"class":46},"sudo",[33,86,87],{"class":54}," nginx",[33,89,90],{"class":50}," -T",[33,92,94],{"class":93},"szBVR"," |",[33,96,97],{"class":46}," sed",[33,99,100],{"class":50}," -n",[33,102,103],{"class":54}," '\u002Fserver_name your-domain.com\u002F,\u002F}\u002Fp'\n",[33,105,107],{"class":35,"line":106},7,[33,108,72],{"emptyLinePlaceholder":71},[33,110,112],{"class":35,"line":111},8,[33,113,114],{"class":39},"# Example working config\n",[33,116,118,121],{"class":35,"line":117},9,[33,119,120],{"class":46},"server",[33,122,123],{"class":54}," {\n",[33,125,127,130,133],{"class":35,"line":126},10,[33,128,129],{"class":46},"    server_name",[33,131,132],{"class":54}," your-domain.com",[33,134,136],{"class":135},"sVt8B",";\n",[33,138,140],{"class":35,"line":139},11,[33,141,72],{"emptyLinePlaceholder":71},[33,143,145,148,151],{"class":35,"line":144},12,[33,146,147],{"class":46},"    location",[33,149,150],{"class":54}," \u002Fstatic\u002F",[33,152,123],{"class":54},[33,154,156,159,162],{"class":35,"line":155},13,[33,157,158],{"class":50},"        alias",[33,160,161],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F",[33,163,136],{"class":135},[33,165,167,170,173],{"class":35,"line":166},14,[33,168,169],{"class":46},"        expires",[33,171,172],{"class":54}," 30d",[33,174,136],{"class":135},[33,176,178,181,184],{"class":35,"line":177},15,[33,179,180],{"class":46},"        access_log",[33,182,183],{"class":54}," off",[33,185,136],{"class":135},[33,187,189],{"class":35,"line":188},16,[33,190,191],{"class":135},"    }\n",[33,193,195],{"class":35,"line":194},17,[33,196,72],{"emptyLinePlaceholder":71},[33,198,200,202,205],{"class":35,"line":199},18,[33,201,147],{"class":46},[33,203,204],{"class":54}," \u002Fmedia\u002F",[33,206,123],{"class":54},[33,208,210,212,215],{"class":35,"line":209},19,[33,211,158],{"class":50},[33,213,214],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F",[33,216,136],{"class":135},[33,218,220],{"class":35,"line":219},20,[33,221,191],{"class":135},[33,223,225],{"class":35,"line":224},21,[33,226,72],{"emptyLinePlaceholder":71},[33,228,230,232,235],{"class":35,"line":229},22,[33,231,147],{"class":46},[33,233,234],{"class":54}," \u002F",[33,236,123],{"class":54},[33,238,240,243,246],{"class":35,"line":239},23,[33,241,242],{"class":46},"        include",[33,244,245],{"class":54}," proxy_params",[33,247,136],{"class":135},[33,249,251,254,257],{"class":35,"line":250},24,[33,252,253],{"class":46},"        proxy_pass",[33,255,256],{"class":54}," http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock",[33,258,136],{"class":135},[33,260,262],{"class":35,"line":261},25,[33,263,191],{"class":135},[33,265,267],{"class":35,"line":266},26,[33,268,269],{"class":135},"}\n",[33,271,273],{"class":35,"line":272},27,[33,274,72],{"emptyLinePlaceholder":71},[33,276,278],{"class":35,"line":277},28,[33,279,280],{"class":39},"# 3) Verify files actually exist on disk\n",[33,282,284,287,290],{"class":35,"line":283},29,[33,285,286],{"class":46},"ls",[33,288,289],{"class":50}," -lah",[33,291,292],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F\n",[33,294,296,298,300],{"class":35,"line":295},30,[33,297,286],{"class":46},[33,299,289],{"class":50},[33,301,302],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F\n",[33,304,306],{"class":35,"line":305},31,[33,307,72],{"emptyLinePlaceholder":71},[33,309,311],{"class":35,"line":310},32,[33,312,313],{"class":39},"# 4) Test and reload Nginx\n",[33,315,317,319,321,324,327,329,332,335],{"class":35,"line":316},33,[33,318,84],{"class":46},[33,320,87],{"class":54},[33,322,323],{"class":50}," -t",[33,325,326],{"class":135}," && ",[33,328,84],{"class":46},[33,330,331],{"class":54}," systemctl",[33,333,334],{"class":54}," reload",[33,336,337],{"class":54}," nginx\n",[14,339,340,341,344],{},"Most 404 cases are caused by a mismatch between the URL path, the Nginx ",[30,342,343],{},"location"," block, and the real filesystem path. Fix those three first.",[18,346,348],{"id":347},"whats-happening","What’s Happening",[14,350,351,352,355,356,359],{},"A 404 on static or media files means the request reaches the server, but the server cannot map that URL to an existing file or route. In production, Nginx usually serves static assets and uploaded media directly, while Gunicorn only handles Flask application requests. If the URL prefix, Nginx ",[30,353,354],{},"alias","\u002F",[30,357,358],{},"root",", filesystem path, or Flask upload\u002Fstatic settings do not match, the request returns 404.",[18,361,363],{"id":362},"step-by-step-guide","Step-by-Step Guide",[365,366,367,402,419,521,565,601,726,810,891,935,986,1034,1086],"ol",{},[368,369,370,374,377,378,399,401],"li",{},[371,372,373],"strong",{},"Identify the exact failing URL",[375,376],"br",{},"Test the same URL the browser is requesting:",[23,379,381],{"className":25,"code":380,"language":27,"meta":28,"style":28},"curl -I https:\u002F\u002Fyour-domain.com\u002Fstatic\u002Fapp.css\ncurl -I https:\u002F\u002Fyour-domain.com\u002Fmedia\u002Fuploads\u002Fexample.jpg\n",[30,382,383,391],{"__ignoreMap":28},[33,384,385,387,389],{"class":35,"line":36},[33,386,47],{"class":46},[33,388,51],{"class":50},[33,390,55],{"class":54},[33,392,393,395,397],{"class":35,"line":43},[33,394,47],{"class":46},[33,396,51],{"class":50},[33,398,65],{"class":54},[375,400],{},"Use browser dev tools if needed to confirm the exact request path.",[368,403,404,407,416,418],{},[371,405,406],{},"Confirm whether the file is static or media",[408,409,410,413],"ul",{},[368,411,412],{},"Static: CSS, JS, built frontend assets, app images",[368,414,415],{},"Media: user uploads, generated files",[375,417],{},"These should usually be served from separate directories.",[368,420,421,424,426,427,429,430,510,512,513,516,517,520],{},[371,422,423],{},"Check your Flask path settings",[375,425],{},"Confirm your application generates URLs that match what Nginx serves.",[375,428],{},"Example Flask app setup:",[23,431,435],{"className":432,"code":433,"language":434,"meta":28,"style":28},"language-python shiki shiki-themes github-light github-dark","from flask import Flask\n\napp = Flask(__name__, static_url_path=\"\u002Fstatic\", static_folder=\"static\")\napp.config[\"UPLOAD_FOLDER\"] = \"\u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\"\n","python",[30,436,437,451,455,494],{"__ignoreMap":28},[33,438,439,442,445,448],{"class":35,"line":36},[33,440,441],{"class":93},"from",[33,443,444],{"class":135}," flask ",[33,446,447],{"class":93},"import",[33,449,450],{"class":135}," Flask\n",[33,452,453],{"class":35,"line":43},[33,454,72],{"emptyLinePlaceholder":71},[33,456,457,460,463,466,469,472,476,478,481,483,486,488,491],{"class":35,"line":58},[33,458,459],{"class":135},"app ",[33,461,462],{"class":93},"=",[33,464,465],{"class":135}," Flask(",[33,467,468],{"class":50},"__name__",[33,470,471],{"class":135},", ",[33,473,475],{"class":474},"s4XuR","static_url_path",[33,477,462],{"class":93},[33,479,480],{"class":54},"\"\u002Fstatic\"",[33,482,471],{"class":135},[33,484,485],{"class":474},"static_folder",[33,487,462],{"class":93},[33,489,490],{"class":54},"\"static\"",[33,492,493],{"class":135},")\n",[33,495,496,499,502,505,507],{"class":35,"line":68},[33,497,498],{"class":135},"app.config[",[33,500,501],{"class":54},"\"UPLOAD_FOLDER\"",[33,503,504],{"class":135},"] ",[33,506,462],{"class":93},[33,508,509],{"class":54}," \"\u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\"\n",[375,511],{},"If templates generate ",[30,514,515],{},"\u002Fstatic\u002F"," but Nginx serves ",[30,518,519],{},"\u002Fassets\u002F",", requests will 404.",[368,522,523,526,528,529],{},[371,524,525],{},"Verify the file exists on disk",[375,527],{},"Check the real file path, not just the directory:",[23,530,532],{"className":25,"code":531,"language":27,"meta":28,"style":28},"ls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F\nls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F\nstat \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css\nstat \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002Fuploads\u002Fexample.jpg\n",[30,533,534,542,550,558],{"__ignoreMap":28},[33,535,536,538,540],{"class":35,"line":36},[33,537,286],{"class":46},[33,539,289],{"class":50},[33,541,292],{"class":54},[33,543,544,546,548],{"class":35,"line":43},[33,545,286],{"class":46},[33,547,289],{"class":50},[33,549,302],{"class":54},[33,551,552,555],{"class":35,"line":58},[33,553,554],{"class":50},"stat",[33,556,557],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css\n",[33,559,560,562],{"class":35,"line":68},[33,561,554],{"class":50},[33,563,564],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002Fuploads\u002Fexample.jpg\n",[368,566,567,570,572,573,587,589,590,471,593,596,597,600],{},[371,568,569],{},"Review the active Nginx server block",[375,571],{},"Dump the loaded config instead of trusting one file:",[23,574,576],{"className":25,"code":575,"language":27,"meta":28,"style":28},"sudo nginx -T\n",[30,577,578],{"__ignoreMap":28},[33,579,580,582,584],{"class":35,"line":36},[33,581,84],{"class":46},[33,583,87],{"class":54},[33,585,586],{"class":50}," -T\n",[375,588],{},"Look for the correct ",[30,591,592],{},"server_name",[30,594,595],{},"location \u002Fstatic\u002F",", and ",[30,598,599],{},"location \u002Fmedia\u002F"," blocks.",[368,602,603,609,611,612,615,616,471,619,621,622,712,714,715],{},[371,604,605,606,608],{},"Use ",[30,607,354],{}," correctly for prefixed URLs",[375,610],{},"For ",[30,613,614],{},"\u002Fstatic\u002F..."," and ",[30,617,618],{},"\u002Fmedia\u002F...",[30,620,354],{}," is usually the safest option:",[23,623,627],{"className":624,"code":625,"language":626,"meta":28,"style":28},"language-nginx shiki shiki-themes github-light github-dark","server {\n    server_name your-domain.com;\n\n    location \u002Fstatic\u002F {\n        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F;\n        expires 30d;\n        access_log off;\n    }\n\n    location \u002Fmedia\u002F {\n        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F;\n    }\n\n    location \u002F {\n        include proxy_params;\n        proxy_pass http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock;\n    }\n}\n","nginx",[30,628,629,634,639,643,648,653,658,663,667,671,676,681,685,689,694,699,704,708],{"__ignoreMap":28},[33,630,631],{"class":35,"line":36},[33,632,633],{},"server {\n",[33,635,636],{"class":35,"line":43},[33,637,638],{},"    server_name your-domain.com;\n",[33,640,641],{"class":35,"line":58},[33,642,72],{"emptyLinePlaceholder":71},[33,644,645],{"class":35,"line":68},[33,646,647],{},"    location \u002Fstatic\u002F {\n",[33,649,650],{"class":35,"line":75},[33,651,652],{},"        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F;\n",[33,654,655],{"class":35,"line":81},[33,656,657],{},"        expires 30d;\n",[33,659,660],{"class":35,"line":106},[33,661,662],{},"        access_log off;\n",[33,664,665],{"class":35,"line":111},[33,666,191],{},[33,668,669],{"class":35,"line":117},[33,670,72],{"emptyLinePlaceholder":71},[33,672,673],{"class":35,"line":126},[33,674,675],{},"    location \u002Fmedia\u002F {\n",[33,677,678],{"class":35,"line":139},[33,679,680],{},"        alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F;\n",[33,682,683],{"class":35,"line":144},[33,684,191],{},[33,686,687],{"class":35,"line":155},[33,688,72],{"emptyLinePlaceholder":71},[33,690,691],{"class":35,"line":166},[33,692,693],{},"    location \u002F {\n",[33,695,696],{"class":35,"line":177},[33,697,698],{},"        include proxy_params;\n",[33,700,701],{"class":35,"line":188},[33,702,703],{},"        proxy_pass http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock;\n",[33,705,706],{"class":35,"line":194},[33,707,191],{},[33,709,710],{"class":35,"line":199},[33,711,269],{},[375,713],{},"Keep trailing slashes consistent:",[408,716,717,721],{},[368,718,719],{},[30,720,595],{},[368,722,723],{},[30,724,725],{},"alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F",[368,727,728,734,736,615,738,740,741,743,744,763,765,766,769,770,778,780,781,799,801,802,807,809],{},[371,729,730,731,733],{},"Avoid incorrect ",[30,732,358],{}," usage",[375,735],{},[30,737,358],{},[30,739,354],{}," do not resolve paths the same way.",[375,742],{},"Example:",[23,745,747],{"className":624,"code":746,"language":626,"meta":28,"style":28},"location \u002Fstatic\u002F {\n    root \u002Fvar\u002Fwww\u002Fyourapp;\n}\n",[30,748,749,754,759],{"__ignoreMap":28},[33,750,751],{"class":35,"line":36},[33,752,753],{},"location \u002Fstatic\u002F {\n",[33,755,756],{"class":35,"line":43},[33,757,758],{},"    root \u002Fvar\u002Fwww\u002Fyourapp;\n",[33,760,761],{"class":35,"line":58},[33,762,269],{},[375,764],{},"A request for ",[30,767,768],{},"\u002Fstatic\u002Fapp.css"," resolves to:",[23,771,776],{"className":772,"code":774,"language":775,"meta":28},[773],"language-text","\u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css\n","text",[30,777,774],{"__ignoreMap":28},[375,779],{},"But:",[23,782,784],{"className":624,"code":783,"language":626,"meta":28,"style":28},"location \u002Fstatic\u002F {\n    alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F;\n}\n",[30,785,786,790,795],{"__ignoreMap":28},[33,787,788],{"class":35,"line":36},[33,789,753],{},[33,791,792],{"class":35,"line":43},[33,793,794],{},"    alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F;\n",[33,796,797],{"class":35,"line":58},[33,798,269],{},[375,800],{},"The same request resolves to:",[23,803,805],{"className":804,"code":774,"language":775,"meta":28},[773],[30,806,774],{"__ignoreMap":28},[375,808],{},"If you mix these patterns incorrectly, the final filesystem path becomes wrong.",[368,811,812,815,817,818,881,883,884,886,887,890],{},[371,813,814],{},"Check directory and file permissions",[375,816],{},"Nginx must be able to traverse parent directories and read the target file:",[23,819,821],{"className":25,"code":820,"language":27,"meta":28,"style":28},"namei -l \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css\nsudo -u www-data test -r \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css && echo readable\nsudo -u www-data test -r \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002Fuploads\u002Fexample.jpg && echo readable\n",[30,822,823,833,860],{"__ignoreMap":28},[33,824,825,828,831],{"class":35,"line":36},[33,826,827],{"class":46},"namei",[33,829,830],{"class":50}," -l",[33,832,557],{"class":54},[33,834,835,837,840,843,846,849,852,854,857],{"class":35,"line":43},[33,836,84],{"class":46},[33,838,839],{"class":50}," -u",[33,841,842],{"class":54}," www-data",[33,844,845],{"class":54}," test",[33,847,848],{"class":50}," -r",[33,850,851],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css",[33,853,326],{"class":135},[33,855,856],{"class":50},"echo",[33,858,859],{"class":54}," readable\n",[33,861,862,864,866,868,870,872,875,877,879],{"class":35,"line":58},[33,863,84],{"class":46},[33,865,839],{"class":50},[33,867,842],{"class":54},[33,869,845],{"class":54},[33,871,848],{"class":50},[33,873,874],{"class":54}," \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002Fuploads\u002Fexample.jpg",[33,876,326],{"class":135},[33,878,856],{"class":50},[33,880,859],{"class":54},[375,882],{},"On some systems, the Nginx user may be ",[30,885,626],{}," instead of ",[30,888,889],{},"www-data",".",[368,892,893,896,898,899,743,901,932,934],{},[371,894,895],{},"Confirm built or collected assets were deployed",[375,897],{},"If your assets are generated during build, make sure the output files exist in the directory referenced by Nginx.",[375,900],{},[23,902,904],{"className":25,"code":903,"language":27,"meta":28,"style":28},"npm run build\ncp -r dist\u002F* \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F\n",[30,905,906,917],{"__ignoreMap":28},[33,907,908,911,914],{"class":35,"line":36},[33,909,910],{"class":46},"npm",[33,912,913],{"class":54}," run",[33,915,916],{"class":54}," build\n",[33,918,919,922,924,927,930],{"class":35,"line":43},[33,920,921],{"class":46},"cp",[33,923,848],{"class":50},[33,925,926],{"class":54}," dist\u002F",[33,928,929],{"class":50},"*",[33,931,292],{"class":54},[375,933],{},"If the files were never copied, URLs may be correct but still return 404.",[368,936,937,940,942,943,945,946,964,966,967],{},[371,938,939],{},"Confirm uploads are written to the same directory Nginx serves",[375,941],{},"If Flask saves uploads to one directory and Nginx serves another, uploaded files will appear missing.",[375,944],{},"Example Flask upload path:",[23,947,949],{"className":432,"code":948,"language":434,"meta":28,"style":28},"app.config[\"UPLOAD_FOLDER\"] = \"\u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002Fuploads\"\n",[30,950,951],{"__ignoreMap":28},[33,952,953,955,957,959,961],{"class":35,"line":36},[33,954,498],{"class":135},[33,956,501],{"class":54},[33,958,504],{"class":135},[33,960,462],{"class":93},[33,962,963],{"class":54}," \"\u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002Fuploads\"\n",[375,965],{},"Matching Nginx:",[23,968,970],{"className":624,"code":969,"language":626,"meta":28,"style":28},"location \u002Fmedia\u002F {\n    alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F;\n}\n",[30,971,972,977,982],{"__ignoreMap":28},[33,973,974],{"class":35,"line":36},[33,975,976],{},"location \u002Fmedia\u002F {\n",[33,978,979],{"class":35,"line":43},[33,980,981],{},"    alias \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F;\n",[33,983,984],{"class":35,"line":58},[33,985,269],{},[368,987,988,991,993,994,996,997,1000,1001,1003,1004],{},[371,989,990],{},"Check if requests are bypassing the static\u002Fmedia blocks",[375,992],{},"If ",[30,995,515],{}," or ",[30,998,999],{},"\u002Fmedia\u002F"," requests are going to Gunicorn, your Nginx location blocks may be missing or in the wrong server block.",[375,1002],{},"Check access patterns and loaded config:",[23,1005,1007],{"className":25,"code":1006,"language":27,"meta":28,"style":28},"grep -R \"location \u002Fstatic\\|location \u002Fmedia\\|server_name\" \u002Fetc\u002Fnginx\u002Fsites-enabled \u002Fetc\u002Fnginx\u002Fconf.d\nsudo nginx -T\n",[30,1008,1009,1026],{"__ignoreMap":28},[33,1010,1011,1014,1017,1020,1023],{"class":35,"line":36},[33,1012,1013],{"class":46},"grep",[33,1015,1016],{"class":50}," -R",[33,1018,1019],{"class":54}," \"location \u002Fstatic\\|location \u002Fmedia\\|server_name\"",[33,1021,1022],{"class":54}," \u002Fetc\u002Fnginx\u002Fsites-enabled",[33,1024,1025],{"class":54}," \u002Fetc\u002Fnginx\u002Fconf.d\n",[33,1027,1028,1030,1032],{"class":35,"line":43},[33,1029,84],{"class":46},[33,1031,87],{"class":54},[33,1033,586],{"class":50},[368,1035,1036,1039,1063,1065,1066],{},[371,1037,1038],{},"Validate and reload Nginx",[23,1040,1042],{"className":25,"code":1041,"language":27,"meta":28,"style":28},"sudo nginx -t\nsudo systemctl reload nginx\n",[30,1043,1044,1053],{"__ignoreMap":28},[33,1045,1046,1048,1050],{"class":35,"line":36},[33,1047,84],{"class":46},[33,1049,87],{"class":54},[33,1051,1052],{"class":50}," -t\n",[33,1054,1055,1057,1059,1061],{"class":35,"line":43},[33,1056,84],{"class":46},[33,1058,331],{"class":54},[33,1060,334],{"class":54},[33,1062,337],{"class":54},[375,1064],{},"Then retest the exact URLs:",[23,1067,1068],{"className":25,"code":380,"language":27,"meta":28,"style":28},[30,1069,1070,1078],{"__ignoreMap":28},[33,1071,1072,1074,1076],{"class":35,"line":36},[33,1073,47],{"class":46},[33,1075,51],{"class":50},[33,1077,55],{"class":54},[33,1079,1080,1082,1084],{"class":35,"line":43},[33,1081,47],{"class":46},[33,1083,51],{"class":50},[33,1085,65],{"class":54},[368,1087,1088,1091,1093,1094,1115,1117],{},[371,1089,1090],{},"Check container mounts or symlinks if applicable",[375,1092],{},"If using Docker or symlinked directories, confirm the files exist where Nginx sees them:",[23,1095,1097],{"className":25,"code":1096,"language":27,"meta":28,"style":28},"ls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F\nls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F\n",[30,1098,1099,1107],{"__ignoreMap":28},[33,1100,1101,1103,1105],{"class":35,"line":36},[33,1102,286],{"class":46},[33,1104,289],{"class":50},[33,1106,292],{"class":54},[33,1108,1109,1111,1113],{"class":35,"line":43},[33,1110,286],{"class":46},[33,1112,289],{"class":50},[33,1114,302],{"class":54},[375,1116],{},"On containerized setups, verify bind mounts inside the running container, not only on the host.",[18,1119,1121],{"id":1120},"common-causes","Common Causes",[408,1123,1124,1138,1152,1168,1174,1180,1190,1196,1209,1215],{},[368,1125,1126,1129,1130,996,1132,1134,1135,1137],{},[371,1127,1128],{},"Wrong Nginx alias path"," → Nginx maps ",[30,1131,515],{},[30,1133,999],{}," to the wrong directory → correct the ",[30,1136,354],{}," to the real absolute path.",[368,1139,1140,1148,1149,1151],{},[371,1141,1142,1143,886,1145,1147],{},"Using ",[30,1144,358],{},[30,1146,354],{}," incorrectly"," → Nginx builds an unexpected final path → switch to ",[30,1150,354],{}," or recalculate the resolved path.",[368,1153,1154,1161,1162,1164,1165,890],{},[371,1155,1156,1157,996,1159],{},"Missing trailing slash in ",[30,1158,354],{},[30,1160,343],{}," → path resolution breaks subtly → keep ",[30,1163,595],{}," with ",[30,1166,1167],{},"alias \u002Fpath\u002Fto\u002Fstatic\u002F",[368,1169,1170,1173],{},[371,1171,1172],{},"Files were never deployed or collected"," → URLs are correct but files do not exist → rebuild or copy assets to the production directory.",[368,1175,1176,1179],{},[371,1177,1178],{},"Media uploads saved to a different folder than Nginx serves"," → uploaded files exist elsewhere → align application upload path with Nginx media path.",[368,1181,1182,1185,1186,1189],{},[371,1183,1184],{},"Wrong server block is active"," → edits were made in one file but another vhost handles the domain → inspect ",[30,1187,1188],{},"nginx -T"," and remove conflicting configs.",[368,1191,1192,1195],{},[371,1193,1194],{},"Permissions on directories or files are too restrictive"," → Nginx cannot traverse or read files and may surface 404 behavior → fix ownership and mode bits.",[368,1197,1198,1201,1202,1205,1206,1208],{},[371,1199,1200],{},"Template-generated URLs are wrong"," → browser requests ",[30,1203,1204],{},"\u002Fstaticfiles\u002F"," while Nginx serves ",[30,1207,515],{}," → correct URL generation in the app or templates.",[368,1210,1211,1214],{},[371,1212,1213],{},"Docker volume or bind mount missing"," → files exist on host but not in the container seen by Nginx → fix the mount path.",[368,1216,1217,1220],{},[371,1218,1219],{},"Symlink target missing or unreadable"," → Nginx follows a broken path → repair the symlink or serve the real directory directly.",[18,1222,1224],{"id":1223},"debugging-section","Debugging Section",[14,1226,1227],{},"Check the active configuration and logs first:",[23,1229,1231],{"className":25,"code":1230,"language":27,"meta":28,"style":28},"sudo nginx -T\nsudo nginx -t\nsudo tail -f \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\nsudo tail -f \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[30,1232,1233,1241,1249,1262],{"__ignoreMap":28},[33,1234,1235,1237,1239],{"class":35,"line":36},[33,1236,84],{"class":46},[33,1238,87],{"class":54},[33,1240,586],{"class":50},[33,1242,1243,1245,1247],{"class":35,"line":43},[33,1244,84],{"class":46},[33,1246,87],{"class":54},[33,1248,1052],{"class":50},[33,1250,1251,1253,1256,1259],{"class":35,"line":58},[33,1252,84],{"class":46},[33,1254,1255],{"class":54}," tail",[33,1257,1258],{"class":50}," -f",[33,1260,1261],{"class":54}," \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\n",[33,1263,1264,1266,1268,1270],{"class":35,"line":68},[33,1265,84],{"class":46},[33,1267,1255],{"class":54},[33,1269,1258],{"class":50},[33,1271,1272],{"class":54}," \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[14,1274,1275],{},"Useful path and permission checks:",[23,1277,1279],{"className":25,"code":1278,"language":27,"meta":28,"style":28},"ls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002F\nls -lah \u002Fvar\u002Fwww\u002Fyourapp\u002Fmedia\u002F\nstat \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css\nnamei -l \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css\nsudo -u www-data test -r \u002Fvar\u002Fwww\u002Fyourapp\u002Fstatic\u002Fapp.css && echo readable\n",[30,1280,1281,1289,1297,1303,1311],{"__ignoreMap":28},[33,1282,1283,1285,1287],{"class":35,"line":36},[33,1284,286],{"class":46},[33,1286,289],{"class":50},[33,1288,292],{"class":54},[33,1290,1291,1293,1295],{"class":35,"line":43},[33,1292,286],{"class":46},[33,1294,289],{"class":50},[33,1296,302],{"class":54},[33,1298,1299,1301],{"class":35,"line":58},[33,1300,554],{"class":50},[33,1302,557],{"class":54},[33,1304,1305,1307,1309],{"class":35,"line":68},[33,1306,827],{"class":46},[33,1308,830],{"class":50},[33,1310,557],{"class":54},[33,1312,1313,1315,1317,1319,1321,1323,1325,1327,1329],{"class":35,"line":75},[33,1314,84],{"class":46},[33,1316,839],{"class":50},[33,1318,842],{"class":54},[33,1320,845],{"class":54},[33,1322,848],{"class":50},[33,1324,851],{"class":54},[33,1326,326],{"class":135},[33,1328,856],{"class":50},[33,1330,859],{"class":54},[14,1332,1333],{},"What to look for:",[408,1335,1336,1348,1351,1357,1360],{},[368,1337,1338,1339,996,1341,1343,1344,1347],{},"Requests for ",[30,1340,614],{},[30,1342,618],{}," returning ",[30,1345,1346],{},"404"," in Nginx access logs",[368,1349,1350],{},"Error log messages showing missing file paths",[368,1352,1353,1354,1356],{},"A different ",[30,1355,592],{}," block handling the request",[368,1358,1359],{},"Gunicorn receiving static\u002Fmedia requests that Nginx should serve",[368,1361,1362],{},"Rendered HTML pointing to the wrong asset prefix",[18,1364,1366],{"id":1365},"checklist","Checklist",[408,1368,1371,1382,1388,1397,1405,1411,1417,1427,1433],{"className":1369},[1370],"contains-task-list",[368,1372,1375,1379,1380],{"className":1373},[1374],"task-list-item",[1376,1377],"input",{"disabled":71,"type":1378},"checkbox"," The failing URL path is identified exactly from browser dev tools or ",[30,1381,47],{},[368,1383,1385,1387],{"className":1384},[1374],[1376,1386],{"disabled":71,"type":1378}," Static files and media files are mapped separately if both are used",[368,1389,1391,1393,1394,1396],{"className":1390},[1374],[1376,1392],{"disabled":71,"type":1378}," Nginx ",[30,1395,595],{}," points to the real static directory",[368,1398,1400,1393,1402,1404],{"className":1399},[1374],[1376,1401],{"disabled":71,"type":1378},[30,1403,599],{}," points to the real upload directory",[368,1406,1408,1410],{"className":1407},[1374],[1376,1409],{"disabled":71,"type":1378}," The exact missing file exists on disk at the resolved path",[368,1412,1414,1416],{"className":1413},[1374],[1376,1415],{"disabled":71,"type":1378}," The Nginx user can read the file and traverse parent directories",[368,1418,1420,1422,1423,1426],{"className":1419},[1374],[1376,1421],{"disabled":71,"type":1378}," ",[30,1424,1425],{},"sudo nginx -t"," passes without errors",[368,1428,1430,1432],{"className":1429},[1374],[1376,1431],{"disabled":71,"type":1378}," Nginx has been reloaded after config changes",[368,1434,1436,1438,1439,996,1442,1445],{"className":1435},[1374],[1376,1437],{"disabled":71,"type":1378}," The browser now returns ",[30,1440,1441],{},"200",[30,1443,1444],{},"304"," for the asset URL",[18,1447,1449],{"id":1448},"related-guides","Related Guides",[408,1451,1452,1459,1465,1471],{},[368,1453,1454],{},[1455,1456,1458],"a",{"href":1457},"\u002Fdeploy\u002Fdeploy-flask-with-nginx-plus-gunicorn-step-by-step-guide","Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)",[368,1460,1461],{},[1455,1462,1464],{"href":1463},"\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production","Flask Static Files Not Loading in Production",[368,1466,1467],{},[1455,1468,1470],{"href":1469},"\u002Fdeploy\u002Fflask-static-and-media-files-production-setup","Flask Static and Media Files Production Setup",[368,1472,1473],{},[1455,1474,1476],{"href":1475},"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","Flask Production Checklist (Everything You Must Do)",[18,1478,1480],{"id":1479},"faq","FAQ",[14,1482,1483,1486,1488],{},[371,1484,1485],{},"Q: Should static and media use the same directory?",[375,1487],{},"\nA: No. Static assets and user uploads should usually be stored and served from separate directories.",[14,1490,1491,1494,1496],{},[371,1492,1493],{},"Q: Why does the browser show 404 but Flask logs show nothing?",[375,1495],{},"\nA: Nginx is likely handling the request directly and returning 404 before it reaches Flask or Gunicorn.",[14,1498,1499,1502,1504],{},[371,1500,1501],{},"Q: Can Gunicorn serve static files?",[375,1503],{},"\nA: It can in limited cases, but production setups should normally use Nginx for static and media delivery.",[14,1506,1507,1510,1512],{},[371,1508,1509],{},"Q: What path should I test first?",[375,1511],{},"\nA: Test the exact URL the browser requests, then map it directly to the expected filesystem path from the Nginx config.",[18,1514,1516],{"id":1515},"final-takeaway","Final Takeaway",[14,1518,1519,1520,471,1522,1524],{},"A 404 on static or media files is usually a path-mapping problem, not an application logic problem. Fix the issue by aligning the request URL, the Nginx location block, and the real filesystem path, then validate with ",[30,1521,47],{},[30,1523,1188],{},", and the Nginx logs.",[1526,1527,1528],"style",{},"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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":28,"searchDepth":43,"depth":43,"links":1530},[1531,1532,1533,1534,1535,1536,1537,1538,1539],{"id":20,"depth":43,"text":21},{"id":347,"depth":43,"text":348},{"id":362,"depth":43,"text":363},{"id":1120,"depth":43,"text":1121},{"id":1223,"depth":43,"text":1224},{"id":1365,"depth":43,"text":1366},{"id":1448,"depth":43,"text":1449},{"id":1479,"depth":43,"text":1480},{"id":1515,"depth":43,"text":1516},"Complete guide on flask 404 on static or media files for Flask production environments.","md",{"ogTitle":5,"ogDescription":1540,"twitterCard":1543,"robots":1544,"canonical":1545},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Ffix-issues\u002Fflask-404-on-static-or-media-files","\u002Ffix-issues\u002Fflask-404-on-static-or-media-files",{"title":5,"description":1540},"fix-issues\u002Fflask-404-on-static-or-media-files","dYitTeU2tfhTKbgqCyvkCwGViArr02Mmjxy1dzgtrU4",1776805765728]