[{"data":1,"prerenderedAt":1830},["ShallowReactive",2],{"\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production":3},{"id":4,"title":5,"body":6,"description":1820,"extension":1821,"meta":1822,"navigation":91,"path":1826,"seo":1827,"stem":1828,"__hash__":1829},"content\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production.md","Flask Static Files Not Loading in Production",{"type":7,"value":8,"toc":1809},"minimark",[9,13,17,22,25,135,138,220,223,287,302,306,309,313,1211,1215,1309,1313,1316,1532,1535,1623,1627,1720,1724,1751,1755,1763,1782,1790,1798,1802,1805],[10,11,5],"h1",{"id":12},"flask-static-files-not-loading-in-production",[14,15,16],"p",{},"If your Flask CSS, JavaScript, images, or other static assets are not loading in production, this guide shows you how to identify the break point and fix it step-by-step. The goal is to make Nginx serve static files correctly and confirm your app generates the right URLs.",[18,19,21],"h2",{"id":20},"quick-fix-quick-setup","Quick Fix \u002F Quick Setup",[14,23,24],{},"Use this first if your production stack is Flask + Gunicorn + Nginx.",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-bash shiki shiki-themes github-light github-dark","# 1) Verify your Flask app exposes the expected static path\npython - \u003C\u003C'PY'\nfrom app import app\nprint('static_folder=', app.static_folder)\nprint('static_url_path=', app.static_url_path)\nPY\n\n# 2) Confirm files exist on disk\nls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\n\n# 3) Add or fix Nginx static mapping\nsudo nano \u002Fetc\u002Fnginx\u002Fsites-available\u002Fmyapp\n","bash","",[33,34,35,44,62,68,74,80,86,93,99,112,117,123],"code",{"__ignoreMap":31},[36,37,40],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sJ8bj","# 1) Verify your Flask app exposes the expected static path\n",[36,45,47,51,55,59],{"class":38,"line":46},2,[36,48,50],{"class":49},"sScJk","python",[36,52,54],{"class":53},"sZZnC"," -",[36,56,58],{"class":57},"szBVR"," \u003C\u003C",[36,60,61],{"class":53},"'PY'\n",[36,63,65],{"class":38,"line":64},3,[36,66,67],{"class":53},"from app import app\n",[36,69,71],{"class":38,"line":70},4,[36,72,73],{"class":53},"print('static_folder=', app.static_folder)\n",[36,75,77],{"class":38,"line":76},5,[36,78,79],{"class":53},"print('static_url_path=', app.static_url_path)\n",[36,81,83],{"class":38,"line":82},6,[36,84,85],{"class":53},"PY\n",[36,87,89],{"class":38,"line":88},7,[36,90,92],{"emptyLinePlaceholder":91},true,"\n",[36,94,96],{"class":38,"line":95},8,[36,97,98],{"class":42},"# 2) Confirm files exist on disk\n",[36,100,102,105,109],{"class":38,"line":101},9,[36,103,104],{"class":49},"ls",[36,106,108],{"class":107},"sj4cs"," -lah",[36,110,111],{"class":53}," \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\n",[36,113,115],{"class":38,"line":114},10,[36,116,92],{"emptyLinePlaceholder":91},[36,118,120],{"class":38,"line":119},11,[36,121,122],{"class":42},"# 3) Add or fix Nginx static mapping\n",[36,124,126,129,132],{"class":38,"line":125},12,[36,127,128],{"class":49},"sudo",[36,130,131],{"class":53}," nano",[36,133,134],{"class":53}," \u002Fetc\u002Fnginx\u002Fsites-available\u002Fmyapp\n",[14,136,137],{},"Example Nginx server block:",[26,139,143],{"className":140,"code":141,"language":142,"meta":31,"style":31},"language-nginx shiki shiki-themes github-light github-dark","server {\n    listen 80;\n    server_name example.com;\n\n    location \u002Fstatic\u002F {\n        alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;\n        expires 7d;\n        add_header Cache-Control \"public\";\n    }\n\n    location \u002F {\n        proxy_pass http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock;\n        include proxy_params;\n    }\n}\n","nginx",[33,144,145,150,155,160,164,169,174,179,184,189,193,198,203,209,214],{"__ignoreMap":31},[36,146,147],{"class":38,"line":39},[36,148,149],{},"server {\n",[36,151,152],{"class":38,"line":46},[36,153,154],{},"    listen 80;\n",[36,156,157],{"class":38,"line":64},[36,158,159],{},"    server_name example.com;\n",[36,161,162],{"class":38,"line":70},[36,163,92],{"emptyLinePlaceholder":91},[36,165,166],{"class":38,"line":76},[36,167,168],{},"    location \u002Fstatic\u002F {\n",[36,170,171],{"class":38,"line":82},[36,172,173],{},"        alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;\n",[36,175,176],{"class":38,"line":88},[36,177,178],{},"        expires 7d;\n",[36,180,181],{"class":38,"line":95},[36,182,183],{},"        add_header Cache-Control \"public\";\n",[36,185,186],{"class":38,"line":101},[36,187,188],{},"    }\n",[36,190,191],{"class":38,"line":114},[36,192,92],{"emptyLinePlaceholder":91},[36,194,195],{"class":38,"line":119},[36,196,197],{},"    location \u002F {\n",[36,199,200],{"class":38,"line":125},[36,201,202],{},"        proxy_pass http:\u002F\u002Funix:\u002Frun\u002Fgunicorn.sock;\n",[36,204,206],{"class":38,"line":205},13,[36,207,208],{},"        include proxy_params;\n",[36,210,212],{"class":38,"line":211},14,[36,213,188],{},[36,215,217],{"class":38,"line":216},15,[36,218,219],{},"}\n",[14,221,222],{},"Apply and test:",[26,224,226],{"className":28,"code":225,"language":30,"meta":31,"style":31},"# 4) Test and reload Nginx\nsudo nginx -t && sudo systemctl reload nginx\n\n# 5) Verify HTTP response\ncurl -I http:\u002F\u002F127.0.0.1\u002Fstatic\u002Fapp.css\ncurl -I https:\u002F\u002Fexample.com\u002Fstatic\u002Fapp.css\n",[33,227,228,233,258,262,267,278],{"__ignoreMap":31},[36,229,230],{"class":38,"line":39},[36,231,232],{"class":42},"# 4) Test and reload Nginx\n",[36,234,235,237,240,243,247,249,252,255],{"class":38,"line":46},[36,236,128],{"class":49},[36,238,239],{"class":53}," nginx",[36,241,242],{"class":107}," -t",[36,244,246],{"class":245},"sVt8B"," && ",[36,248,128],{"class":49},[36,250,251],{"class":53}," systemctl",[36,253,254],{"class":53}," reload",[36,256,257],{"class":53}," nginx\n",[36,259,260],{"class":38,"line":64},[36,261,92],{"emptyLinePlaceholder":91},[36,263,264],{"class":38,"line":70},[36,265,266],{"class":42},"# 5) Verify HTTP response\n",[36,268,269,272,275],{"class":38,"line":76},[36,270,271],{"class":49},"curl",[36,273,274],{"class":107}," -I",[36,276,277],{"class":53}," http:\u002F\u002F127.0.0.1\u002Fstatic\u002Fapp.css\n",[36,279,280,282,284],{"class":38,"line":82},[36,281,271],{"class":49},[36,283,274],{"class":107},[36,285,286],{"class":53}," https:\u002F\u002Fexample.com\u002Fstatic\u002Fapp.css\n",[14,288,289,290,293,294,297,298,301],{},"In most production setups, Nginx should serve static files directly. The fastest fix is usually correcting the Nginx ",[33,291,292],{},"location"," block, ",[33,295,296],{},"alias"," path, file permissions, or the Flask ",[33,299,300],{},"static_url_path",".",[18,303,305],{"id":304},"whats-happening","What’s Happening",[14,307,308],{},"In production, Gunicorn usually handles dynamic Flask requests while Nginx serves static files directly. Static files fail when the requested URL does not match the Nginx mapping, the on-disk path is wrong, or Nginx cannot read the files. Another common failure is that Flask templates generate the wrong asset URL, so the browser requests a path that does not exist.",[18,310,312],{"id":311},"step-by-step-guide","Step-by-Step Guide",[314,315,316,359,418,489,610,720,801,851,885,1000,1017,1080,1123,1170,1189],"ol",{},[317,318,319,323,326,327,330,331,333,334],"li",{},[320,321,322],"strong",{},"Check the failing asset URL in the browser",[324,325],"br",{},"Open browser dev tools and inspect the ",[320,328,329],{},"Network"," tab. Identify the exact asset path and HTTP status.",[324,332],{},"Look for:",[335,336,337,346,356],"ul",{},[317,338,339,342,343],{},[33,340,341],{},"\u002Fstatic\u002F..."," vs ",[33,344,345],{},"\u002Fassets\u002F...",[317,347,348,351,352,355],{},[33,349,350],{},"404",", ",[33,353,354],{},"403",", or unexpected redirects",[317,357,358],{},"wrong domain or protocol",[317,360,361,364,366,367],{},[320,362,363],{},"Verify the file exists on disk",[324,365],{},"Confirm the static directory and target file are present on the server.",[26,368,370],{"className":28,"code":369,"language":30,"meta":31,"style":31},"ls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\nls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fcss\u002Fapp.css\nfind \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic -maxdepth 3 -type f | head -50\n",[33,371,372,380,389],{"__ignoreMap":31},[36,373,374,376,378],{"class":38,"line":39},[36,375,104],{"class":49},[36,377,108],{"class":107},[36,379,111],{"class":53},[36,381,382,384,386],{"class":38,"line":46},[36,383,104],{"class":49},[36,385,108],{"class":107},[36,387,388],{"class":53}," \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fcss\u002Fapp.css\n",[36,390,391,394,397,400,403,406,409,412,415],{"class":38,"line":64},[36,392,393],{"class":49},"find",[36,395,396],{"class":53}," \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic",[36,398,399],{"class":107}," -maxdepth",[36,401,402],{"class":107}," 3",[36,404,405],{"class":107}," -type",[36,407,408],{"class":53}," f",[36,410,411],{"class":57}," |",[36,413,414],{"class":49}," head",[36,416,417],{"class":107}," -50\n",[317,419,420,423,425,426,429,430,432,433,450,452,453],{},[320,421,422],{},"Confirm Flask generates the correct static URL",[324,424],{},"In templates, use ",[33,427,428],{},"url_for()"," instead of hardcoded paths.",[324,431],{},"Correct:",[26,434,438],{"className":435,"code":436,"language":437,"meta":31,"style":31},"language-jinja2 shiki shiki-themes github-light github-dark","\u003Clink rel=\"stylesheet\" href=\"{{ url_for('static', filename='css\u002Fapp.css') }}\">\n\u003Cscript src=\"{{ url_for('static', filename='js\u002Fapp.js') }}\">\u003C\u002Fscript>\n","jinja2",[33,439,440,445],{"__ignoreMap":31},[36,441,442],{"class":38,"line":39},[36,443,444],{},"\u003Clink rel=\"stylesheet\" href=\"{{ url_for('static', filename='css\u002Fapp.css') }}\">\n",[36,446,447],{"class":38,"line":46},[36,448,449],{},"\u003Cscript src=\"{{ url_for('static', filename='js\u002Fapp.js') }}\">\u003C\u002Fscript>\n",[324,451],{},"Avoid:",[26,454,458],{"className":455,"code":456,"language":457,"meta":31,"style":31},"language-html shiki shiki-themes github-light github-dark","\u003Clink rel=\"stylesheet\" href=\"\u002Fcss\u002Fapp.css\">\n","html",[33,459,460],{"__ignoreMap":31},[36,461,462,465,469,472,475,478,481,483,486],{"class":38,"line":39},[36,463,464],{"class":245},"\u003C",[36,466,468],{"class":467},"s9eBZ","link",[36,470,471],{"class":49}," rel",[36,473,474],{"class":245},"=",[36,476,477],{"class":53},"\"stylesheet\"",[36,479,480],{"class":49}," href",[36,482,474],{"class":245},[36,484,485],{"class":53},"\"\u002Fcss\u002Fapp.css\"",[36,487,488],{"class":245},">\n",[317,490,491,494,496,497,530,532,533,593,595,596,598,599,602,603,606,607,301],{},[320,492,493],{},"Check Flask static settings",[324,495],{},"Print the active static configuration.",[26,498,500],{"className":28,"code":499,"language":30,"meta":31,"style":31},"python - \u003C\u003C'PY'\nfrom app import app\nprint(app.static_folder)\nprint(app.static_url_path)\nPY\n",[33,501,502,512,516,521,526],{"__ignoreMap":31},[36,503,504,506,508,510],{"class":38,"line":39},[36,505,50],{"class":49},[36,507,54],{"class":53},[36,509,58],{"class":57},[36,511,61],{"class":53},[36,513,514],{"class":38,"line":46},[36,515,67],{"class":53},[36,517,518],{"class":38,"line":64},[36,519,520],{"class":53},"print(app.static_folder)\n",[36,522,523],{"class":38,"line":70},[36,524,525],{"class":53},"print(app.static_url_path)\n",[36,527,528],{"class":38,"line":76},[36,529,85],{"class":53},[324,531],{},"Example Flask app:",[26,534,537],{"className":535,"code":536,"language":50,"meta":31,"style":31},"language-python shiki shiki-themes github-light github-dark","from flask import Flask\n\napp = Flask(__name__, static_folder=\"static\", static_url_path=\"\u002Fstatic\")\n",[33,538,539,553,557],{"__ignoreMap":31},[36,540,541,544,547,550],{"class":38,"line":39},[36,542,543],{"class":57},"from",[36,545,546],{"class":245}," flask ",[36,548,549],{"class":57},"import",[36,551,552],{"class":245}," Flask\n",[36,554,555],{"class":38,"line":46},[36,556,92],{"emptyLinePlaceholder":91},[36,558,559,562,564,567,570,572,576,578,581,583,585,587,590],{"class":38,"line":64},[36,560,561],{"class":245},"app ",[36,563,474],{"class":57},[36,565,566],{"class":245}," Flask(",[36,568,569],{"class":107},"__name__",[36,571,351],{"class":245},[36,573,575],{"class":574},"s4XuR","static_folder",[36,577,474],{"class":57},[36,579,580],{"class":53},"\"static\"",[36,582,351],{"class":245},[36,584,300],{"class":574},[36,586,474],{"class":57},[36,588,589],{"class":53},"\"\u002Fstatic\"",[36,591,592],{"class":245},")\n",[324,594],{},"If ",[33,597,300],{}," is ",[33,600,601],{},"\u002Fassets",", Nginx must serve ",[33,604,605],{},"\u002Fassets\u002F",", not ",[33,608,609],{},"\u002Fstatic\u002F",[317,611,612,615,617,618,631,633,634,698,700,701],{},[320,613,614],{},"Inspect the Nginx static mapping",[324,616],{},"Open the active site config:",[26,619,621],{"className":28,"code":620,"language":30,"meta":31,"style":31},"sudo nano \u002Fetc\u002Fnginx\u002Fsites-available\u002Fmyapp\n",[33,622,623],{"__ignoreMap":31},[36,624,625,627,629],{"class":38,"line":39},[36,626,128],{"class":49},[36,628,131],{"class":53},[36,630,134],{"class":53},[324,632],{},"Recommended setup:",[26,635,636],{"className":140,"code":141,"language":142,"meta":31,"style":31},[33,637,638,642,646,650,654,658,662,666,670,674,678,682,686,690,694],{"__ignoreMap":31},[36,639,640],{"class":38,"line":39},[36,641,149],{},[36,643,644],{"class":38,"line":46},[36,645,154],{},[36,647,648],{"class":38,"line":64},[36,649,159],{},[36,651,652],{"class":38,"line":70},[36,653,92],{"emptyLinePlaceholder":91},[36,655,656],{"class":38,"line":76},[36,657,168],{},[36,659,660],{"class":38,"line":82},[36,661,173],{},[36,663,664],{"class":38,"line":88},[36,665,178],{},[36,667,668],{"class":38,"line":95},[36,669,183],{},[36,671,672],{"class":38,"line":101},[36,673,188],{},[36,675,676],{"class":38,"line":114},[36,677,92],{"emptyLinePlaceholder":91},[36,679,680],{"class":38,"line":119},[36,681,197],{},[36,683,684],{"class":38,"line":125},[36,685,202],{},[36,687,688],{"class":38,"line":205},[36,689,208],{},[36,691,692],{"class":38,"line":211},[36,693,188],{},[36,695,696],{"class":38,"line":216},[36,697,219],{},[324,699],{},"Critical details:",[335,702,703,709,714],{},[317,704,705,708],{},[33,706,707],{},"location \u002Fstatic\u002F"," must match the requested URL prefix",[317,710,711,713],{},[33,712,296],{}," should point to the real deployed directory",[317,715,716,717],{},"keep the trailing slash in ",[33,718,719],{},"alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;",[317,721,722,728,730,731,733,734,301,737,739,740,759,761,762,764,765,767,768,786,788,789,792,793],{},[320,723,724,725,727],{},"Use ",[33,726,296],{}," carefully",[324,729],{},"For Flask static folders, ",[33,732,296],{}," is usually safer than ",[33,735,736],{},"root",[324,738],{},"Good:",[26,741,743],{"className":140,"code":742,"language":142,"meta":31,"style":31},"location \u002Fstatic\u002F {\n    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;\n}\n",[33,744,745,750,755],{"__ignoreMap":31},[36,746,747],{"class":38,"line":39},[36,748,749],{},"location \u002Fstatic\u002F {\n",[36,751,752],{"class":38,"line":46},[36,753,754],{},"    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;\n",[36,756,757],{"class":38,"line":64},[36,758,219],{},[324,760],{},"If using ",[33,763,736],{},", verify the final resolved path is correct.",[324,766],{},"Example:",[26,769,771],{"className":140,"code":770,"language":142,"meta":31,"style":31},"location \u002Fstatic\u002F {\n    root \u002Fvar\u002Fwww\u002Fmyapp;\n}\n",[33,772,773,777,782],{"__ignoreMap":31},[36,774,775],{"class":38,"line":39},[36,776,749],{},[36,778,779],{"class":38,"line":46},[36,780,781],{},"    root \u002Fvar\u002Fwww\u002Fmyapp;\n",[36,783,784],{"class":38,"line":64},[36,785,219],{},[324,787],{},"That makes ",[33,790,791],{},"\u002Fstatic\u002Fapp.css"," resolve to:",[26,794,799],{"className":795,"code":797,"language":798,"meta":31},[796],"language-text","\u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fapp.css\n","text",[33,800,797],{"__ignoreMap":31},[317,802,803,806,808,809,823,825,826],{},[320,804,805],{},"Validate the Nginx configuration",[324,807],{},"Test before reload.",[26,810,812],{"className":28,"code":811,"language":30,"meta":31,"style":31},"sudo nginx -t\n",[33,813,814],{"__ignoreMap":31},[36,815,816,818,820],{"class":38,"line":39},[36,817,128],{"class":49},[36,819,239],{"class":53},[36,821,822],{"class":107}," -t\n",[324,824],{},"If needed, print the loaded config:",[26,827,829],{"className":28,"code":828,"language":30,"meta":31,"style":31},"sudo nginx -T | sed -n '\u002Fserver_name example.com\u002F,\u002F}\u002Fp'\n",[33,830,831],{"__ignoreMap":31},[36,832,833,835,837,840,842,845,848],{"class":38,"line":39},[36,834,128],{"class":49},[36,836,239],{"class":53},[36,838,839],{"class":107}," -T",[36,841,411],{"class":57},[36,843,844],{"class":49}," sed",[36,846,847],{"class":107}," -n",[36,849,850],{"class":53}," '\u002Fserver_name example.com\u002F,\u002F}\u002Fp'\n",[317,852,853,856,858,859],{},[320,854,855],{},"Reload Nginx",[324,857],{},"After fixing the config:",[26,860,862],{"className":28,"code":861,"language":30,"meta":31,"style":31},"sudo systemctl reload nginx\nsudo systemctl status nginx\n",[33,863,864,874],{"__ignoreMap":31},[36,865,866,868,870,872],{"class":38,"line":39},[36,867,128],{"class":49},[36,869,251],{"class":53},[36,871,254],{"class":53},[36,873,257],{"class":53},[36,875,876,878,880,883],{"class":38,"line":46},[36,877,128],{"class":49},[36,879,251],{"class":53},[36,881,882],{"class":53}," status",[36,884,257],{"class":53},[317,886,887,890,892,893,895,896,933,935,936,978,980,981],{},[320,888,889],{},"Verify file and directory permissions",[324,891],{},"Nginx must be able to traverse parent directories and read files.",[324,894],{},"Check effective path permissions:",[26,897,899],{"className":28,"code":898,"language":30,"meta":31,"style":31},"namei -l \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fcss\u002Fapp.css\nstat \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fcss\u002Fapp.css\nps aux | grep nginx\n",[33,900,901,911,918],{"__ignoreMap":31},[36,902,903,906,909],{"class":38,"line":39},[36,904,905],{"class":49},"namei",[36,907,908],{"class":107}," -l",[36,910,388],{"class":53},[36,912,913,916],{"class":38,"line":46},[36,914,915],{"class":107},"stat",[36,917,388],{"class":53},[36,919,920,923,926,928,931],{"class":38,"line":64},[36,921,922],{"class":49},"ps",[36,924,925],{"class":53}," aux",[36,927,411],{"class":57},[36,929,930],{"class":49}," grep",[36,932,257],{"class":53},[324,934],{},"Typical safe permissions:",[26,937,939],{"className":28,"code":938,"language":30,"meta":31,"style":31},"sudo chmod 755 \u002Fvar\u002Fwww\nsudo chmod 755 \u002Fvar\u002Fwww\u002Fmyapp\nsudo chmod -R 755 \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\n",[33,940,941,954,965],{"__ignoreMap":31},[36,942,943,945,948,951],{"class":38,"line":39},[36,944,128],{"class":49},[36,946,947],{"class":53}," chmod",[36,949,950],{"class":107}," 755",[36,952,953],{"class":53}," \u002Fvar\u002Fwww\n",[36,955,956,958,960,962],{"class":38,"line":46},[36,957,128],{"class":49},[36,959,947],{"class":53},[36,961,950],{"class":107},[36,963,964],{"class":53}," \u002Fvar\u002Fwww\u002Fmyapp\n",[36,966,967,969,971,974,976],{"class":38,"line":64},[36,968,128],{"class":49},[36,970,947],{"class":53},[36,972,973],{"class":107}," -R",[36,975,950],{"class":107},[36,977,111],{"class":53},[324,979],{},"If ownership is incorrect:",[26,982,984],{"className":28,"code":983,"language":30,"meta":31,"style":31},"sudo chown -R root:root \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\n",[33,985,986],{"__ignoreMap":31},[36,987,988,990,993,995,998],{"class":38,"line":39},[36,989,128],{"class":49},[36,991,992],{"class":53}," chown",[36,994,973],{"class":107},[36,996,997],{"class":53}," root:root",[36,999,111],{"class":53},[317,1001,1002,1005,1007,1008,1010,1011,1013,1014,1016],{},[320,1003,1004],{},"Confirm requests are not being proxied to Gunicorn",[324,1006],{},"If Gunicorn receives ",[33,1009,609],{}," requests, Nginx routing is wrong.",[324,1012],{},"Check whether the generic proxy block is catching static requests before the static block. Your Nginx config should include a dedicated ",[33,1015,707],{}," block.",[317,1018,1019,1022,1024,1025,1027,1028,1077,1079],{},[320,1020,1021],{},"Check Docker or multi-container setups",[324,1023],{},"If using Docker, make sure the static directory exists in the container actually serving the files.",[324,1026],{},"Example checks:",[26,1029,1031],{"className":28,"code":1030,"language":30,"meta":31,"style":31},"docker ps\ndocker exec -it nginx-container ls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\ndocker exec -it nginx-container ls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fcss\u002Fapp.css\n",[33,1032,1033,1041,1061],{"__ignoreMap":31},[36,1034,1035,1038],{"class":38,"line":39},[36,1036,1037],{"class":49},"docker",[36,1039,1040],{"class":53}," ps\n",[36,1042,1043,1045,1048,1051,1054,1057,1059],{"class":38,"line":46},[36,1044,1037],{"class":49},[36,1046,1047],{"class":53}," exec",[36,1049,1050],{"class":107}," -it",[36,1052,1053],{"class":53}," nginx-container",[36,1055,1056],{"class":53}," ls",[36,1058,108],{"class":107},[36,1060,111],{"class":53},[36,1062,1063,1065,1067,1069,1071,1073,1075],{"class":38,"line":64},[36,1064,1037],{"class":49},[36,1066,1047],{"class":53},[36,1068,1050],{"class":107},[36,1070,1053],{"class":53},[36,1072,1056],{"class":53},[36,1074,108],{"class":107},[36,1076,388],{"class":53},[324,1078],{},"If Nginx runs in a separate container, mount the same static directory into that container.",[317,1081,1082,1085,1087,1088,1109,1111,1112],{},[320,1083,1084],{},"Check direct HTTP responses",[324,1086],{},"Test from localhost and through the public domain.",[26,1089,1091],{"className":28,"code":1090,"language":30,"meta":31,"style":31},"curl -I http:\u002F\u002F127.0.0.1\u002Fstatic\u002Fapp.css\ncurl -I https:\u002F\u002Fexample.com\u002Fstatic\u002Fapp.css\n",[33,1092,1093,1101],{"__ignoreMap":31},[36,1094,1095,1097,1099],{"class":38,"line":39},[36,1096,271],{"class":49},[36,1098,274],{"class":107},[36,1100,277],{"class":53},[36,1102,1103,1105,1107],{"class":38,"line":46},[36,1104,271],{"class":49},[36,1106,274],{"class":107},[36,1108,286],{"class":53},[324,1110],{},"Expected result:",[335,1113,1114],{},[317,1115,1116,1119,1120],{},[33,1117,1118],{},"200 OK"," or ",[33,1121,1122],{},"304 Not Modified",[317,1124,1125,1128,1130,1131,1161,1163,1164,1166,1167,1169],{},[320,1126,1127],{},"Inspect rendered HTML",[324,1129],{},"Confirm the app is emitting the URL you expect.",[26,1132,1134],{"className":28,"code":1133,"language":30,"meta":31,"style":31},"curl -s https:\u002F\u002Fexample.com | grep -o '\u002Fstatic\u002F[^\" ]*' | head\n",[33,1135,1136],{"__ignoreMap":31},[36,1137,1138,1140,1143,1146,1148,1150,1153,1156,1158],{"class":38,"line":39},[36,1139,271],{"class":49},[36,1141,1142],{"class":107}," -s",[36,1144,1145],{"class":53}," https:\u002F\u002Fexample.com",[36,1147,411],{"class":57},[36,1149,930],{"class":49},[36,1151,1152],{"class":107}," -o",[36,1154,1155],{"class":53}," '\u002Fstatic\u002F[^\" ]*'",[36,1157,411],{"class":57},[36,1159,1160],{"class":49}," head\n",[324,1162],{},"If the HTML points to ",[33,1165,345],{}," but Nginx only serves ",[33,1168,341],{},", align them.",[317,1171,1172,1175,1177,1178,767,1180],{},[320,1173,1174],{},"Clear stale browser references",[324,1176],{},"If the config is fixed but the browser still requests old filenames, clear cache or use a cache-busting query string.",[324,1179],{},[26,1181,1183],{"className":435,"code":1182,"language":437,"meta":31,"style":31},"\u003Clink rel=\"stylesheet\" href=\"{{ url_for('static', filename='css\u002Fapp.css') }}?v=20260421\">\n",[33,1184,1185],{"__ignoreMap":31},[36,1186,1187],{"class":38,"line":39},[36,1188,1182],{},[317,1190,1191,1194,1196,1197],{},[320,1192,1193],{},"Check case sensitivity, symlinks, and security controls",[324,1195],{},"If only some files fail:",[335,1198,1199,1202,1205,1208],{},[317,1200,1201],{},"verify exact filename case",[317,1203,1204],{},"verify symlink targets",[317,1206,1207],{},"check SELinux or AppArmor if enabled",[317,1209,1210],{},"confirm the file was actually deployed",[18,1212,1214],{"id":1213},"common-causes","Common Causes",[335,1216,1217,1229,1240,1249,1255,1269,1279,1285,1294,1303],{},[317,1218,1219,1222,1223,1225,1226,1228],{},[320,1220,1221],{},"Incorrect Nginx alias path"," → Nginx points ",[33,1224,609],{}," to the wrong directory → update ",[33,1227,296],{}," to the real deployed static folder and reload Nginx.",[317,1230,1231,1234,1235,1237,1238,301],{},[320,1232,1233],{},"Missing trailing slash in alias"," → Nginx resolves file paths incorrectly → use ",[33,1236,719],{}," for ",[33,1239,707],{},[317,1241,1242,1245,1246,301],{},[320,1243,1244],{},"Templates use hardcoded asset paths"," → browser requests the wrong URL → use ",[33,1247,1248],{},"url_for('static', filename='...')",[317,1250,1251,1254],{},[320,1252,1253],{},"Static files were not deployed"," → files exist locally but not on the server → sync or copy the static directory during deployment.",[317,1256,1257,1262,1263,1265,1266,1268],{},[320,1258,1259,1260],{},"Wrong Flask ",[33,1261,300],{}," → app generates ",[33,1264,345],{}," while Nginx serves ",[33,1267,341],{}," → align Flask and Nginx URL paths.",[317,1270,1271,1274,1275,1278],{},[320,1272,1273],{},"Permissions blocked"," → Nginx user cannot read files or traverse parent directories → fix ownership and ",[33,1276,1277],{},"chmod"," on directories and files.",[317,1280,1281,1284],{},[320,1282,1283],{},"Docker volume mismatch"," → static files exist in one container but not in the serving container → mount the same static directory where Nginx serves from.",[317,1286,1287,1290,1291,301],{},[320,1288,1289],{},"Nginx config not loaded"," → you edited one file but another server block is active → verify active config with ",[33,1292,1293],{},"nginx -T",[317,1295,1296,1299,1300,1302],{},[320,1297,1298],{},"Static requests are proxied to Gunicorn"," → no dedicated Nginx location block handles ",[33,1301,609],{}," → add the static location before the generic proxy location.",[317,1304,1305,1308],{},[320,1306,1307],{},"Browser cache serves stale references"," → HTML points to old filenames after deploy → clear cache or use versioned static assets.",[18,1310,1312],{"id":1311},"debugging-section","Debugging Section",[14,1314,1315],{},"Check these commands in order:",[26,1317,1319],{"className":28,"code":1318,"language":30,"meta":31,"style":31},"ls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\nfind \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic -maxdepth 3 -type f | head -50\n\npython - \u003C\u003C'PY'\nfrom app import app\nprint(app.static_folder)\nprint(app.static_url_path)\nPY\n\nsudo nginx -t\nsudo nginx -T | sed -n '\u002Fserver_name example.com\u002F,\u002F}\u002Fp'\n\ncurl -I http:\u002F\u002F127.0.0.1\u002Fstatic\u002Fapp.css\ncurl -I https:\u002F\u002Fexample.com\u002Fstatic\u002Fapp.css\ncurl -s https:\u002F\u002Fexample.com | grep -o '\u002Fstatic\u002F[^\" ]*' | head\n\nsudo tail -n 100 \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\nsudo tail -n 100 \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n\nnamei -l \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fcss\u002Fapp.css\nstat \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002Fcss\u002Fapp.css\n\nps aux | grep nginx\nsudo systemctl status nginx\n",[33,1320,1321,1329,1349,1353,1363,1367,1371,1375,1379,1383,1391,1407,1411,1419,1427,1447,1452,1468,1482,1487,1496,1503,1508,1521],{"__ignoreMap":31},[36,1322,1323,1325,1327],{"class":38,"line":39},[36,1324,104],{"class":49},[36,1326,108],{"class":107},[36,1328,111],{"class":53},[36,1330,1331,1333,1335,1337,1339,1341,1343,1345,1347],{"class":38,"line":46},[36,1332,393],{"class":49},[36,1334,396],{"class":53},[36,1336,399],{"class":107},[36,1338,402],{"class":107},[36,1340,405],{"class":107},[36,1342,408],{"class":53},[36,1344,411],{"class":57},[36,1346,414],{"class":49},[36,1348,417],{"class":107},[36,1350,1351],{"class":38,"line":64},[36,1352,92],{"emptyLinePlaceholder":91},[36,1354,1355,1357,1359,1361],{"class":38,"line":70},[36,1356,50],{"class":49},[36,1358,54],{"class":53},[36,1360,58],{"class":57},[36,1362,61],{"class":53},[36,1364,1365],{"class":38,"line":76},[36,1366,67],{"class":53},[36,1368,1369],{"class":38,"line":82},[36,1370,520],{"class":53},[36,1372,1373],{"class":38,"line":88},[36,1374,525],{"class":53},[36,1376,1377],{"class":38,"line":95},[36,1378,85],{"class":53},[36,1380,1381],{"class":38,"line":101},[36,1382,92],{"emptyLinePlaceholder":91},[36,1384,1385,1387,1389],{"class":38,"line":114},[36,1386,128],{"class":49},[36,1388,239],{"class":53},[36,1390,822],{"class":107},[36,1392,1393,1395,1397,1399,1401,1403,1405],{"class":38,"line":119},[36,1394,128],{"class":49},[36,1396,239],{"class":53},[36,1398,839],{"class":107},[36,1400,411],{"class":57},[36,1402,844],{"class":49},[36,1404,847],{"class":107},[36,1406,850],{"class":53},[36,1408,1409],{"class":38,"line":125},[36,1410,92],{"emptyLinePlaceholder":91},[36,1412,1413,1415,1417],{"class":38,"line":205},[36,1414,271],{"class":49},[36,1416,274],{"class":107},[36,1418,277],{"class":53},[36,1420,1421,1423,1425],{"class":38,"line":211},[36,1422,271],{"class":49},[36,1424,274],{"class":107},[36,1426,286],{"class":53},[36,1428,1429,1431,1433,1435,1437,1439,1441,1443,1445],{"class":38,"line":216},[36,1430,271],{"class":49},[36,1432,1142],{"class":107},[36,1434,1145],{"class":53},[36,1436,411],{"class":57},[36,1438,930],{"class":49},[36,1440,1152],{"class":107},[36,1442,1155],{"class":53},[36,1444,411],{"class":57},[36,1446,1160],{"class":49},[36,1448,1450],{"class":38,"line":1449},16,[36,1451,92],{"emptyLinePlaceholder":91},[36,1453,1455,1457,1460,1462,1465],{"class":38,"line":1454},17,[36,1456,128],{"class":49},[36,1458,1459],{"class":53}," tail",[36,1461,847],{"class":107},[36,1463,1464],{"class":107}," 100",[36,1466,1467],{"class":53}," \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\n",[36,1469,1471,1473,1475,1477,1479],{"class":38,"line":1470},18,[36,1472,128],{"class":49},[36,1474,1459],{"class":53},[36,1476,847],{"class":107},[36,1478,1464],{"class":107},[36,1480,1481],{"class":53}," \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[36,1483,1485],{"class":38,"line":1484},19,[36,1486,92],{"emptyLinePlaceholder":91},[36,1488,1490,1492,1494],{"class":38,"line":1489},20,[36,1491,905],{"class":49},[36,1493,908],{"class":107},[36,1495,388],{"class":53},[36,1497,1499,1501],{"class":38,"line":1498},21,[36,1500,915],{"class":107},[36,1502,388],{"class":53},[36,1504,1506],{"class":38,"line":1505},22,[36,1507,92],{"emptyLinePlaceholder":91},[36,1509,1511,1513,1515,1517,1519],{"class":38,"line":1510},23,[36,1512,922],{"class":49},[36,1514,925],{"class":53},[36,1516,411],{"class":57},[36,1518,930],{"class":49},[36,1520,257],{"class":53},[36,1522,1524,1526,1528,1530],{"class":38,"line":1523},24,[36,1525,128],{"class":49},[36,1527,251],{"class":53},[36,1529,882],{"class":53},[36,1531,257],{"class":53},[14,1533,1534],{},"What to look for:",[335,1536,1537,1550,1572,1591,1610],{},[317,1538,1539,1542],{},[320,1540,1541],{},"Access log",[335,1543,1544,1547],{},[317,1545,1546],{},"confirms whether the request reaches Nginx",[317,1548,1549],{},"shows actual status code returned",[317,1551,1552,1555],{},[320,1553,1554],{},"Error log",[335,1556,1557,1560,1563,1569],{},[317,1558,1559],{},"permission denied",[317,1561,1562],{},"file not found",[317,1564,1565,1566,1568],{},"bad ",[33,1567,296],{}," mapping",[317,1570,1571],{},"wrong virtual host handling the request",[317,1573,1574,1577],{},[320,1575,1576],{},"Rendered HTML",[335,1578,1579,1585,1588],{},[317,1580,1581,1582,1584],{},"incorrect ",[33,1583,609],{}," path",[317,1586,1587],{},"wrong domain",[317,1589,1590],{},"mixed-content issues on HTTPS",[317,1592,1593,1598],{},[320,1594,1595,1597],{},[33,1596,1293],{}," output",[335,1599,1600,1603],{},[317,1601,1602],{},"confirms the expected server block is loaded",[317,1604,1605,1606,1609],{},"reveals duplicate or conflicting ",[33,1607,1608],{},"server_name"," blocks",[317,1611,1612,1615],{},[320,1613,1614],{},"Gunicorn behavior",[335,1616,1617],{},[317,1618,1619,1620,1622],{},"if ",[33,1621,609],{}," requests hit Gunicorn, reverse proxy routing is wrong",[18,1624,1626],{"id":1625},"checklist","Checklist",[335,1628,1631,1640,1649,1658,1666,1677,1687,1693,1699,1714],{"className":1629},[1630],"contains-task-list",[317,1632,1635,1639],{"className":1633},[1634],"task-list-item",[1636,1637],"input",{"disabled":91,"type":1638},"checkbox"," Static files exist in the deployed filesystem path.",[317,1641,1643,1645,1646,301],{"className":1642},[1634],[1636,1644],{"disabled":91,"type":1638}," Flask templates use ",[33,1647,1648],{},"url_for('static', filename=...)",[317,1650,1652,1654,1655,1657],{"className":1651},[1634],[1636,1653],{"disabled":91,"type":1638}," Flask ",[33,1656,300],{}," matches the public URL prefix.",[317,1659,1661,1663,1664,1016],{"className":1660},[1634],[1636,1662],{"disabled":91,"type":1638}," Nginx has a ",[33,1665,707],{},[317,1667,1669,1671,1672,1119,1674,1676],{"className":1668},[1634],[1636,1670],{"disabled":91,"type":1638}," The Nginx ",[33,1673,296],{},[33,1675,736],{}," path matches the real static directory.",[317,1678,1680,1682,1683,1686],{"className":1679},[1634],[1636,1681],{"disabled":91,"type":1638}," ",[33,1684,1685],{},"nginx -t"," passes without errors.",[317,1688,1690,1692],{"className":1689},[1634],[1636,1691],{"disabled":91,"type":1638}," Nginx has been reloaded after config changes.",[317,1694,1696,1698],{"className":1695},[1634],[1636,1697],{"disabled":91,"type":1638}," File and directory permissions allow Nginx to read static assets.",[317,1700,1702,1704,1705,1707,1708,1119,1711,301],{"className":1701},[1634],[1636,1703],{"disabled":91,"type":1638}," Direct requests to ",[33,1706,341],{}," return ",[33,1709,1710],{},"200",[33,1712,1713],{},"304",[317,1715,1717,1719],{"className":1716},[1634],[1636,1718],{"disabled":91,"type":1638}," Browser dev tools no longer show failed CSS, JS, or image requests.",[18,1721,1723],{"id":1722},"related-guides","Related Guides",[335,1725,1726,1733,1739,1745],{},[317,1727,1728],{},[1729,1730,1732],"a",{"href":1731},"\u002Fdeploy\u002Fdeploy-flask-with-nginx-plus-gunicorn-step-by-step-guide","Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)",[317,1734,1735],{},[1729,1736,1738],{"href":1737},"\u002Fdeploy\u002Fflask-static-and-media-files-production-setup","Flask Static and Media Files Production Setup",[317,1740,1741],{},[1729,1742,1744],{"href":1743},"\u002Ffix-issues\u002Fflask-404-on-static-or-media-files","Flask 404 on Static or Media Files",[317,1746,1747],{},[1729,1748,1750],{"href":1749},"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","Flask Production Checklist (Everything You Must Do)",[18,1752,1754],{"id":1753},"faq","FAQ",[14,1756,1757,1760,1762],{},[320,1758,1759],{},"Q: Should I let Flask serve static files in production?",[324,1761],{},"\nA: Usually no. Use Nginx to serve static files directly and keep Gunicorn for app requests.",[14,1764,1765,1768,1770,1771,1119,1773,1775,1776,1778,1779,1781],{},[320,1766,1767],{},"Q: What status code usually indicates this problem?",[324,1769],{},"\nA: Most often ",[33,1772,350],{},[33,1774,354],{},". A ",[33,1777,350],{}," usually means wrong path or missing files. A ",[33,1780,354],{}," usually means permissions.",[14,1783,1784,1787,1789],{},[320,1785,1786],{},"Q: Why do assets load on localhost but fail on the live domain?",[324,1788],{},"\nA: Your local development server may serve static files automatically, while production depends on Nginx or container routing.",[14,1791,1792,1795,1797],{},[320,1793,1794],{},"Q: Can a bad DNS or HTTPS setup cause missing static files?",[324,1796],{},"\nA: Yes. If assets are requested from the wrong domain, blocked by mixed-content rules, or routed to the wrong vhost, they may fail even when files exist.",[18,1799,1801],{"id":1800},"final-takeaway","Final Takeaway",[14,1803,1804],{},"Static file failures in Flask production usually come from one of four issues: wrong URL, wrong Nginx mapping, missing files, or read permissions. Validate the request path, file location, and active Nginx config in that order to resolve the issue quickly.",[1806,1807,1808],"style",{},"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 .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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":31,"searchDepth":46,"depth":46,"links":1810},[1811,1812,1813,1814,1815,1816,1817,1818,1819],{"id":20,"depth":46,"text":21},{"id":304,"depth":46,"text":305},{"id":311,"depth":46,"text":312},{"id":1213,"depth":46,"text":1214},{"id":1311,"depth":46,"text":1312},{"id":1625,"depth":46,"text":1626},{"id":1722,"depth":46,"text":1723},{"id":1753,"depth":46,"text":1754},{"id":1800,"depth":46,"text":1801},"Complete guide on flask static files not loading in production for Flask production environments.","md",{"ogTitle":5,"ogDescription":1820,"twitterCard":1823,"robots":1824,"canonical":1825},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production","\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production",{"title":5,"description":1820},"fix-issues\u002Fflask-static-files-not-loading-in-production","Ao-aP-zp4_uvd3ItwEREBsKXEmQLC7dhcDZWwCrMrrA",1776805765051]