[{"data":1,"prerenderedAt":1307},["ShallowReactive",2],{"\u002Freference\u002Fflask-static-vs-media-files-explained":3},{"id":4,"title":5,"body":6,"description":1297,"extension":1298,"meta":1299,"navigation":122,"path":1303,"seo":1304,"stem":1305,"__hash__":1306},"content\u002Freference\u002Fflask-static-vs-media-files-explained.md","Flask Static vs Media Files Explained",{"type":7,"value":8,"toc":1278},"minimark",[9,13,17,22,25,90,180,234,263,274,278,281,296,300,792,796,849,853,856,861,874,878,893,897,926,929,950,954,1003,1007,1029,1031,1042,1046,1066,1070,1085,1088,1111,1115,1184,1188,1215,1219,1227,1235,1243,1251,1259,1267,1271,1274],[10,11,5],"h1",{"id":12},"flask-static-vs-media-files-explained",[14,15,16],"p",{},"If you're trying to understand why CSS, JavaScript, images, or user uploads behave differently in Flask production, this guide shows you how to configure them correctly. The outcome is a working file layout and routing setup that avoids broken assets, missing uploads, and incorrect Nginx routing.",[18,19,21],"h2",{"id":20},"quick-fix-quick-setup","Quick Fix \u002F Quick Setup",[14,23,24],{},"Use separate directories and URL paths immediately:",[26,27,32],"pre",{"className":28,"code":29,"language":30,"meta":31,"style":31},"language-bash shiki shiki-themes github-light github-dark","# Example production layout\nsudo mkdir -p \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\nsudo mkdir -p \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\nsudo chown -R www-data:www-data \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n","bash","",[33,34,35,44,62,74],"code",{"__ignoreMap":31},[36,37,40],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sJ8bj","# Example production layout\n",[36,45,47,51,55,59],{"class":38,"line":46},2,[36,48,50],{"class":49},"sScJk","sudo",[36,52,54],{"class":53},"sZZnC"," mkdir",[36,56,58],{"class":57},"sj4cs"," -p",[36,60,61],{"class":53}," \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\n",[36,63,65,67,69,71],{"class":38,"line":64},3,[36,66,50],{"class":49},[36,68,54],{"class":53},[36,70,58],{"class":57},[36,72,73],{"class":53}," \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n",[36,75,77,79,82,85,88],{"class":38,"line":76},4,[36,78,50],{"class":49},[36,80,81],{"class":53}," chown",[36,83,84],{"class":57}," -R",[36,86,87],{"class":53}," www-data:www-data",[36,89,73],{"class":53},[26,91,95],{"className":92,"code":93,"language":94,"meta":31,"style":31},"language-python shiki shiki-themes github-light github-dark","# app.py\nfrom flask import Flask\n\napp = Flask(__name__, static_folder='static', static_url_path='\u002Fstatic')\napp.config['UPLOAD_FOLDER'] = '\u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia'\n","python",[33,96,97,102,118,124,163],{"__ignoreMap":31},[36,98,99],{"class":38,"line":39},[36,100,101],{"class":42},"# app.py\n",[36,103,104,108,112,115],{"class":38,"line":46},[36,105,107],{"class":106},"szBVR","from",[36,109,111],{"class":110},"sVt8B"," flask ",[36,113,114],{"class":106},"import",[36,116,117],{"class":110}," Flask\n",[36,119,120],{"class":38,"line":64},[36,121,123],{"emptyLinePlaceholder":122},true,"\n",[36,125,126,129,132,135,138,141,145,147,150,152,155,157,160],{"class":38,"line":76},[36,127,128],{"class":110},"app ",[36,130,131],{"class":106},"=",[36,133,134],{"class":110}," Flask(",[36,136,137],{"class":57},"__name__",[36,139,140],{"class":110},", ",[36,142,144],{"class":143},"s4XuR","static_folder",[36,146,131],{"class":106},[36,148,149],{"class":53},"'static'",[36,151,140],{"class":110},[36,153,154],{"class":143},"static_url_path",[36,156,131],{"class":106},[36,158,159],{"class":53},"'\u002Fstatic'",[36,161,162],{"class":110},")\n",[36,164,166,169,172,175,177],{"class":38,"line":165},5,[36,167,168],{"class":110},"app.config[",[36,170,171],{"class":53},"'UPLOAD_FOLDER'",[36,173,174],{"class":110},"] ",[36,176,131],{"class":106},[36,178,179],{"class":53}," '\u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia'\n",[26,181,185],{"className":182,"code":183,"language":184,"meta":31,"style":31},"language-nginx shiki shiki-themes github-light github-dark","# \u002Fstatic\u002F -> app static files\nlocation \u002Fstatic\u002F {\n    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;\n}\n\n# \u002Fmedia\u002F -> uploaded files\nlocation \u002Fmedia\u002F {\n    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\u002F;\n}\n","nginx",[33,186,187,192,197,202,207,211,217,223,229],{"__ignoreMap":31},[36,188,189],{"class":38,"line":39},[36,190,191],{},"# \u002Fstatic\u002F -> app static files\n",[36,193,194],{"class":38,"line":46},[36,195,196],{},"location \u002Fstatic\u002F {\n",[36,198,199],{"class":38,"line":64},[36,200,201],{},"    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;\n",[36,203,204],{"class":38,"line":76},[36,205,206],{},"}\n",[36,208,209],{"class":38,"line":165},[36,210,123],{"emptyLinePlaceholder":122},[36,212,214],{"class":38,"line":213},6,[36,215,216],{},"# \u002Fmedia\u002F -> uploaded files\n",[36,218,220],{"class":38,"line":219},7,[36,221,222],{},"location \u002Fmedia\u002F {\n",[36,224,226],{"class":38,"line":225},8,[36,227,228],{},"    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\u002F;\n",[36,230,232],{"class":38,"line":231},9,[36,233,206],{},[26,235,237],{"className":28,"code":236,"language":30,"meta":31,"style":31},"sudo nginx -t && sudo systemctl reload nginx\n",[33,238,239],{"__ignoreMap":31},[36,240,241,243,246,249,252,254,257,260],{"class":38,"line":39},[36,242,50],{"class":49},[36,244,245],{"class":53}," nginx",[36,247,248],{"class":57}," -t",[36,250,251],{"class":110}," && ",[36,253,50],{"class":49},[36,255,256],{"class":53}," systemctl",[36,258,259],{"class":53}," reload",[36,261,262],{"class":53}," nginx\n",[14,264,265,266,269,270,273],{},"Use ",[33,267,268],{},"static"," for versioned app assets such as CSS, JS, and logos. Use ",[33,271,272],{},"media"," for files created or uploaded after deployment. Do not mix them into the same directory or URL path.",[18,275,277],{"id":276},"whats-happening","What’s Happening",[14,279,280],{},"Static files are application assets shipped with the release: CSS, JavaScript, fonts, and fixed images. Media files are runtime-generated or user-uploaded content: avatars, documents, reports, and attachments.",[14,282,283,284,287,288,291,292,295],{},"In production, Flask should not serve either type directly when Nginx is available. Nginx should map ",[33,285,286],{},"\u002Fstatic\u002F"," and ",[33,289,290],{},"\u002Fmedia\u002F"," to separate filesystem paths. Most production issues happen when both types share one folder, one URL prefix, or incorrect Nginx ",[33,293,294],{},"alias"," settings.",[18,297,299],{"id":298},"step-by-step-guide","Step-by-Step Guide",[301,302,303,327,360,377,429,451,541,561,618,643,694,723,760],"ol",{},[304,305,306,310,313,314],"li",{},[307,308,309],"strong",{},"Reserve separate URL paths",[311,312],"br",{},"Use:",[315,316,317,322],"ul",{},[304,318,319,321],{},[33,320,286],{}," for application assets",[304,323,324,326],{},[33,325,290],{}," for uploaded or generated files",[304,328,329,332,334,335],{},[307,330,331],{},"Create separate filesystem directories",[311,333],{},"Example:",[26,336,338],{"className":28,"code":337,"language":30,"meta":31,"style":31},"sudo mkdir -p \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\nsudo mkdir -p \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n",[33,339,340,350],{"__ignoreMap":31},[36,341,342,344,346,348],{"class":38,"line":39},[36,343,50],{"class":49},[36,345,54],{"class":53},[36,347,58],{"class":57},[36,349,61],{"class":53},[36,351,352,354,356,358],{"class":38,"line":46},[36,353,50],{"class":49},[36,355,54],{"class":53},[36,357,58],{"class":57},[36,359,73],{"class":53},[304,361,362,365,367,368],{},[307,363,364],{},"Keep media outside the release path",[311,366],{},"If your deployment replaces the app directory, uploaded files stored there will be lost. Use a persistent path:",[26,369,371],{"className":28,"code":370,"language":30,"meta":31,"style":31},"\u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n",[33,372,373],{"__ignoreMap":31},[36,374,375],{"class":38,"line":39},[36,376,370],{"class":49},[304,378,379,382],{},[307,380,381],{},"Configure Flask static settings explicitly",[26,383,385],{"className":92,"code":384,"language":94,"meta":31,"style":31},"from flask import Flask\n\napp = Flask(__name__, static_folder='static', static_url_path='\u002Fstatic')\n",[33,386,387,397,401],{"__ignoreMap":31},[36,388,389,391,393,395],{"class":38,"line":39},[36,390,107],{"class":106},[36,392,111],{"class":110},[36,394,114],{"class":106},[36,396,117],{"class":110},[36,398,399],{"class":38,"line":46},[36,400,123],{"emptyLinePlaceholder":122},[36,402,403,405,407,409,411,413,415,417,419,421,423,425,427],{"class":38,"line":64},[36,404,128],{"class":110},[36,406,131],{"class":106},[36,408,134],{"class":110},[36,410,137],{"class":57},[36,412,140],{"class":110},[36,414,144],{"class":143},[36,416,131],{"class":106},[36,418,149],{"class":53},[36,420,140],{"class":110},[36,422,154],{"class":143},[36,424,131],{"class":106},[36,426,159],{"class":53},[36,428,162],{"class":110},[304,430,431,434],{},[307,432,433],{},"Configure upload storage explicitly",[26,435,437],{"className":92,"code":436,"language":94,"meta":31,"style":31},"app.config['UPLOAD_FOLDER'] = '\u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia'\n",[33,438,439],{"__ignoreMap":31},[36,440,441,443,445,447,449],{"class":38,"line":39},[36,442,168],{"class":110},[36,444,171],{"class":53},[36,446,174],{"class":110},[36,448,131],{"class":106},[36,450,179],{"class":53},[304,452,453,456,334,458],{},[307,454,455],{},"Save uploaded files into the media directory",[311,457],{},[26,459,461],{"className":92,"code":460,"language":94,"meta":31,"style":31},"import os\nfrom werkzeug.utils import secure_filename\nfrom flask import request\n\nfile = request.files['file']\nfilename = secure_filename(file.filename)\nfile.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))\n",[33,462,463,470,482,493,497,514,529],{"__ignoreMap":31},[36,464,465,467],{"class":38,"line":39},[36,466,114],{"class":106},[36,468,469],{"class":110}," os\n",[36,471,472,474,477,479],{"class":38,"line":46},[36,473,107],{"class":106},[36,475,476],{"class":110}," werkzeug.utils ",[36,478,114],{"class":106},[36,480,481],{"class":110}," secure_filename\n",[36,483,484,486,488,490],{"class":38,"line":64},[36,485,107],{"class":106},[36,487,111],{"class":110},[36,489,114],{"class":106},[36,491,492],{"class":110}," request\n",[36,494,495],{"class":38,"line":76},[36,496,123],{"emptyLinePlaceholder":122},[36,498,499,502,505,508,511],{"class":38,"line":165},[36,500,501],{"class":143},"file",[36,503,504],{"class":106}," =",[36,506,507],{"class":110}," request.files[",[36,509,510],{"class":53},"'file'",[36,512,513],{"class":110},"]\n",[36,515,516,519,521,524,526],{"class":38,"line":213},[36,517,518],{"class":110},"filename ",[36,520,131],{"class":106},[36,522,523],{"class":110}," secure_filename(",[36,525,501],{"class":143},[36,527,528],{"class":110},".filename)\n",[36,530,531,533,536,538],{"class":38,"line":219},[36,532,501],{"class":143},[36,534,535],{"class":110},".save(os.path.join(app.config[",[36,537,171],{"class":53},[36,539,540],{"class":110},"], filename))\n",[304,542,543,546,548,549,558,560],{},[307,544,545],{},"Do not save uploads inside app source directories",[311,547],{},"Avoid paths such as:",[26,550,552],{"className":28,"code":551,"language":30,"meta":31,"style":31},"\u002Fvar\u002Fwww\u002Fmyapp\u002Fcurrent\u002Fapp\u002Fstatic\u002Fuploads\n",[33,553,554],{"__ignoreMap":31},[36,555,556],{"class":38,"line":39},[36,557,551],{"class":49},[311,559],{},"Use a persistent media path instead.",[304,562,563,566,265,568,570,571,574,575],{},[307,564,565],{},"Add Nginx location blocks for direct file serving",[311,567],{},[33,569,294],{},", not ",[33,572,573],{},"proxy_pass",", for static and media:",[26,576,578],{"className":182,"code":577,"language":184,"meta":31,"style":31},"location \u002Fstatic\u002F {\n    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\u002F;\n    expires 30d;\n    access_log off;\n}\n\nlocation \u002Fmedia\u002F {\n    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\u002F;\n}\n",[33,579,580,584,588,593,598,602,606,610,614],{"__ignoreMap":31},[36,581,582],{"class":38,"line":39},[36,583,196],{},[36,585,586],{"class":38,"line":46},[36,587,201],{},[36,589,590],{"class":38,"line":64},[36,591,592],{},"    expires 30d;\n",[36,594,595],{"class":38,"line":76},[36,596,597],{},"    access_log off;\n",[36,599,600],{"class":38,"line":165},[36,601,206],{},[36,603,604],{"class":38,"line":213},[36,605,123],{"emptyLinePlaceholder":122},[36,607,608],{"class":38,"line":219},[36,609,222],{},[36,611,612],{"class":38,"line":225},[36,613,228],{},[36,615,616],{"class":38,"line":231},[36,617,206],{},[304,619,620,623,625,626],{},[307,621,622],{},"Use trailing slashes correctly",[311,624],{},"If the location ends with a slash, the alias should also end with a slash:",[26,627,629],{"className":182,"code":628,"language":184,"meta":31,"style":31},"location \u002Fmedia\u002F {\n    alias \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\u002F;\n}\n",[33,630,631,635,639],{"__ignoreMap":31},[36,632,633],{"class":38,"line":39},[36,634,222],{},[36,636,637],{"class":38,"line":46},[36,638,228],{},[36,640,641],{"class":38,"line":64},[36,642,206],{},[304,644,645,648,650,651],{},[307,646,647],{},"Set ownership and permissions",[311,649],{},"The app process must be able to write to media. Nginx must be able to read static and media:",[26,652,654],{"className":28,"code":653,"language":30,"meta":31,"style":31},"sudo chown -R www-data:www-data \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\nsudo chmod -R 755 \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\nsudo chmod -R 755 \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n",[33,655,656,668,682],{"__ignoreMap":31},[36,657,658,660,662,664,666],{"class":38,"line":39},[36,659,50],{"class":49},[36,661,81],{"class":53},[36,663,84],{"class":57},[36,665,87],{"class":53},[36,667,73],{"class":53},[36,669,670,672,675,677,680],{"class":38,"line":46},[36,671,50],{"class":49},[36,673,674],{"class":53}," chmod",[36,676,84],{"class":57},[36,678,679],{"class":57}," 755",[36,681,61],{"class":53},[36,683,684,686,688,690,692],{"class":38,"line":64},[36,685,50],{"class":49},[36,687,674],{"class":53},[36,689,84],{"class":57},[36,691,679],{"class":57},[36,693,73],{"class":53},[304,695,696,699],{},[307,697,698],{},"Validate and reload Nginx",[26,700,702],{"className":28,"code":701,"language":30,"meta":31,"style":31},"sudo nginx -t\nsudo systemctl reload nginx\n",[33,703,704,713],{"__ignoreMap":31},[36,705,706,708,710],{"class":38,"line":39},[36,707,50],{"class":49},[36,709,245],{"class":53},[36,711,712],{"class":57}," -t\n",[36,714,715,717,719,721],{"class":38,"line":46},[36,716,50],{"class":49},[36,718,256],{"class":53},[36,720,259],{"class":53},[36,722,262],{"class":53},[304,724,725,728,753,755,756,759],{},[307,726,727],{},"Test both paths directly",[26,729,731],{"className":28,"code":730,"language":30,"meta":31,"style":31},"curl -I http:\u002F\u002Flocalhost\u002Fstatic\u002Fapp.css\ncurl -I http:\u002F\u002Flocalhost\u002Fmedia\u002Ftest.jpg\n",[33,732,733,744],{"__ignoreMap":31},[36,734,735,738,741],{"class":38,"line":39},[36,736,737],{"class":49},"curl",[36,739,740],{"class":57}," -I",[36,742,743],{"class":53}," http:\u002F\u002Flocalhost\u002Fstatic\u002Fapp.css\n",[36,745,746,748,750],{"class":38,"line":46},[36,747,737],{"class":49},[36,749,740],{"class":57},[36,751,752],{"class":53}," http:\u002F\u002Flocalhost\u002Fmedia\u002Ftest.jpg\n",[311,754],{},"Expected result: ",[33,757,758],{},"200 OK"," for known files.",[304,761,762,765,767,768],{},[307,763,764],{},"Handle Docker persistence correctly",[311,766],{},"If using containers, keep static in the image or in a mounted path, and mount media as a persistent volume:",[26,769,773],{"className":770,"code":771,"language":772,"meta":31,"style":31},"language-yaml shiki shiki-themes github-light github-dark","volumes:\n  - .\u002Fmedia:\u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n","yaml",[33,774,775,784],{"__ignoreMap":31},[36,776,777,781],{"class":38,"line":39},[36,778,780],{"class":779},"s9eBZ","volumes",[36,782,783],{"class":110},":\n",[36,785,786,789],{"class":38,"line":46},[36,787,788],{"class":110},"  - ",[36,790,791],{"class":53},".\u002Fmedia:\u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n",[18,793,795],{"id":794},"common-causes","Common Causes",[315,797,798,804,810,816,826,832,843],{},[304,799,800,803],{},[307,801,802],{},"Static and media are stored in the same directory"," → deployments, cache rules, and permissions become inconsistent → split them into separate folders and URL prefixes.",[304,805,806,809],{},[307,807,808],{},"Uploads are saved inside the project release directory"," → files disappear on redeploy → move media to a persistent path outside the release.",[304,811,812,815],{},[307,813,814],{},"Nginx alias points to the wrong path"," → requests return 404 even though files exist → verify the alias target and trailing slash usage.",[304,817,818,821,822,825],{},[307,819,820],{},"The app writes uploads to one path while Nginx serves another"," → uploads succeed but files are not reachable → align ",[33,823,824],{},"UPLOAD_FOLDER"," with the Nginx media alias.",[304,827,828,831],{},[307,829,830],{},"Permissions prevent writes to media"," → uploads fail or create empty paths → grant write access to the app user and read access to Nginx.",[304,833,834,837,838,287,840,842],{},[307,835,836],{},"Static files are proxied to Gunicorn instead of served by Nginx"," → poor performance or path confusion → add dedicated Nginx location blocks for ",[33,839,286],{},[33,841,290],{},".",[304,844,845,848],{},[307,846,847],{},"Docker stores uploads in an ephemeral filesystem"," → files vanish after restart → mount a persistent volume for media.",[18,850,852],{"id":851},"debugging-section","Debugging Section",[14,854,855],{},"Check configuration, paths, and logs in this order.",[857,858,860],"h3",{"id":859},"validate-nginx-config","Validate Nginx config",[26,862,864],{"className":28,"code":863,"language":30,"meta":31,"style":31},"sudo nginx -t\n",[33,865,866],{"__ignoreMap":31},[36,867,868,870,872],{"class":38,"line":39},[36,869,50],{"class":49},[36,871,245],{"class":53},[36,873,712],{"class":57},[857,875,877],{"id":876},"reload-after-changes","Reload after changes",[26,879,881],{"className":28,"code":880,"language":30,"meta":31,"style":31},"sudo systemctl reload nginx\n",[33,882,883],{"__ignoreMap":31},[36,884,885,887,889,891],{"class":38,"line":39},[36,886,50],{"class":49},[36,888,256],{"class":53},[36,890,259],{"class":53},[36,892,262],{"class":53},[857,894,896],{"id":895},"watch-nginx-logs","Watch Nginx logs",[26,898,900],{"className":28,"code":899,"language":30,"meta":31,"style":31},"sudo tail -f \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\nsudo tail -f \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\n",[33,901,902,915],{"__ignoreMap":31},[36,903,904,906,909,912],{"class":38,"line":39},[36,905,50],{"class":49},[36,907,908],{"class":53}," tail",[36,910,911],{"class":57}," -f",[36,913,914],{"class":53}," \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[36,916,917,919,921,923],{"class":38,"line":46},[36,918,50],{"class":49},[36,920,908],{"class":53},[36,922,911],{"class":57},[36,924,925],{"class":53}," \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\n",[14,927,928],{},"What to look for:",[315,930,931,944,947],{},[304,932,933,936,937,940,941],{},[33,934,935],{},"404"," on ",[33,938,939],{},"\u002Fstatic\u002F..."," or ",[33,942,943],{},"\u002Fmedia\u002F...",[304,945,946],{},"permission-related errors",[304,948,949],{},"wrong resolved paths",[857,951,953],{"id":952},"verify-directories-exist","Verify directories exist",[26,955,957],{"className":28,"code":956,"language":30,"meta":31,"style":31},"ls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fstatic\nls -lah \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\nfind \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia -maxdepth 2 -type f | head\n",[33,958,959,969,977],{"__ignoreMap":31},[36,960,961,964,967],{"class":38,"line":39},[36,962,963],{"class":49},"ls",[36,965,966],{"class":57}," -lah",[36,968,61],{"class":53},[36,970,971,973,975],{"class":38,"line":46},[36,972,963],{"class":49},[36,974,966],{"class":57},[36,976,73],{"class":53},[36,978,979,982,985,988,991,994,997,1000],{"class":38,"line":64},[36,980,981],{"class":49},"find",[36,983,984],{"class":53}," \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia",[36,986,987],{"class":57}," -maxdepth",[36,989,990],{"class":57}," 2",[36,992,993],{"class":57}," -type",[36,995,996],{"class":53}," f",[36,998,999],{"class":106}," |",[36,1001,1002],{"class":49}," head\n",[857,1004,1006],{"id":1005},"check-path-traversal-and-permissions","Check path traversal and permissions",[26,1008,1010],{"className":28,"code":1009,"language":30,"meta":31,"style":31},"namei -l \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\nstat \u002Fvar\u002Fwww\u002Fmyapp\u002Fmedia\n",[33,1011,1012,1022],{"__ignoreMap":31},[36,1013,1014,1017,1020],{"class":38,"line":39},[36,1015,1016],{"class":49},"namei",[36,1018,1019],{"class":57}," -l",[36,1021,73],{"class":53},[36,1023,1024,1027],{"class":38,"line":46},[36,1025,1026],{"class":57},"stat",[36,1028,73],{"class":53},[14,1030,928],{},[315,1032,1033,1036,1039],{},[304,1034,1035],{},"execute permission on parent directories",[304,1037,1038],{},"write permission for the app user",[304,1040,1041],{},"readable files for Nginx",[857,1043,1045],{"id":1044},"test-direct-file-access","Test direct file access",[26,1047,1048],{"className":28,"code":730,"language":30,"meta":31,"style":31},[33,1049,1050,1058],{"__ignoreMap":31},[36,1051,1052,1054,1056],{"class":38,"line":39},[36,1053,737],{"class":49},[36,1055,740],{"class":57},[36,1057,743],{"class":53},[36,1059,1060,1062,1064],{"class":38,"line":46},[36,1061,737],{"class":49},[36,1063,740],{"class":57},[36,1065,752],{"class":53},[857,1067,1069],{"id":1068},"inspect-flask-config-values","Inspect Flask config values",[26,1071,1073],{"className":28,"code":1072,"language":30,"meta":31,"style":31},"python -c \"from app import app; print(app.static_folder); print(app.static_url_path); print(app.config.get('UPLOAD_FOLDER'))\"\n",[33,1074,1075],{"__ignoreMap":31},[36,1076,1077,1079,1082],{"class":38,"line":39},[36,1078,94],{"class":49},[36,1080,1081],{"class":57}," -c",[36,1083,1084],{"class":53}," \"from app import app; print(app.static_folder); print(app.static_url_path); print(app.config.get('UPLOAD_FOLDER'))\"\n",[14,1086,1087],{},"What to confirm:",[315,1089,1090,1095,1103],{},[304,1091,1092,1094],{},[33,1093,144],{}," points to the intended static directory",[304,1096,1097,1099,1100],{},[33,1098,154],{}," is ",[33,1101,1102],{},"\u002Fstatic",[304,1104,1105,1107,1108,1110],{},[33,1106,824],{}," matches the Nginx ",[33,1109,290],{}," alias target",[18,1112,1114],{"id":1113},"checklist","Checklist",[315,1116,1119,1128,1134,1143,1157,1163,1169,1175],{"className":1117},[1118],"contains-task-list",[304,1120,1123,1127],{"className":1121},[1122],"task-list-item",[1124,1125],"input",{"disabled":122,"type":1126},"checkbox"," Static and media use different URL prefixes.",[304,1129,1131,1133],{"className":1130},[1122],[1124,1132],{"disabled":122,"type":1126}," Static and media use different filesystem directories.",[304,1135,1137,1139,1140,1142],{"className":1136},[1122],[1124,1138],{"disabled":122,"type":1126}," Flask ",[33,1141,824],{}," points to the media directory.",[304,1144,1146,1148,1149,287,1151,1153,1154,1156],{"className":1145},[1122],[1124,1147],{"disabled":122,"type":1126}," Nginx serves ",[33,1150,286],{},[33,1152,290],{}," with correct ",[33,1155,294],{}," paths.",[304,1158,1160,1162],{"className":1159},[1122],[1124,1161],{"disabled":122,"type":1126}," The application process can write to media.",[304,1164,1166,1168],{"className":1165},[1122],[1124,1167],{"disabled":122,"type":1126}," Nginx can read static and media files.",[304,1170,1172,1174],{"className":1171},[1122],[1124,1173],{"disabled":122,"type":1126}," Uploaded files remain present after redeploy.",[304,1176,1178,1180,1181,842],{"className":1177},[1122],[1124,1179],{"disabled":122,"type":1126}," Direct requests to known static and media files return ",[33,1182,1183],{},"200",[18,1185,1187],{"id":1186},"related-guides","Related Guides",[315,1189,1190,1197,1203,1209],{},[304,1191,1192],{},[1193,1194,1196],"a",{"href":1195},"\u002Fdeploy\u002Fflask-static-and-media-files-production-setup","Flask Static and Media Files Production Setup",[304,1198,1199],{},[1193,1200,1202],{"href":1201},"\u002Ffix-issues\u002Fflask-static-files-not-loading-in-production","Flask Static Files Not Loading in Production",[304,1204,1205],{},[1193,1206,1208],{"href":1207},"\u002Ffix-issues\u002Fflask-404-on-static-or-media-files","Flask 404 on Static or Media Files",[304,1210,1211],{},[1193,1212,1214],{"href":1213},"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","Flask Production Checklist (Everything You Must Do)",[18,1216,1218],{"id":1217},"faq","FAQ",[14,1220,1221,1224,1226],{},[307,1222,1223],{},"Q: What is the difference between static and media files in Flask?",[311,1225],{},"\nA: Static files are shipped with the app release. Media files are uploaded or generated after deployment.",[14,1228,1229,1232,1234],{},[307,1230,1231],{},"Q: Should uploaded files go into the static folder?",[311,1233],{},"\nA: No. Uploaded files should use a separate media directory and URL path.",[14,1236,1237,1240,1242],{},[307,1238,1239],{},"Q: Why does Nginx need separate locations for static and media?",[311,1241],{},"\nA: They often have different paths, permissions, and caching behavior, so separate locations prevent routing mistakes.",[14,1244,1245,1248,1250],{},[307,1246,1247],{},"Q: Can Flask serve static and media in production?",[311,1249],{},"\nA: It can, but Nginx should serve them in production for better performance and simpler routing.",[14,1252,1253,1256,1258],{},[307,1254,1255],{},"Q: Can media files be public?",[311,1257],{},"\nA: Yes, if intended. If access must be restricted, serve them through authenticated application logic instead of a public Nginx alias.",[14,1260,1261,1264,1266],{},[307,1262,1263],{},"Q: Why do my uploads disappear after container restart?",[311,1265],{},"\nA: They are likely stored in the container filesystem instead of a persistent mounted volume.",[18,1268,1270],{"id":1269},"final-takeaway","Final Takeaway",[14,1272,1273],{},"Static files are deployment-time assets. Media files are runtime-created content. Keep them separated in both URL paths and filesystem directories, and let Nginx serve each from the correct location.",[1275,1276,1277],"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 .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 .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}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":31,"searchDepth":46,"depth":46,"links":1279},[1280,1281,1282,1283,1284,1293,1294,1295,1296],{"id":20,"depth":46,"text":21},{"id":276,"depth":46,"text":277},{"id":298,"depth":46,"text":299},{"id":794,"depth":46,"text":795},{"id":851,"depth":46,"text":852,"children":1285},[1286,1287,1288,1289,1290,1291,1292],{"id":859,"depth":64,"text":860},{"id":876,"depth":64,"text":877},{"id":895,"depth":64,"text":896},{"id":952,"depth":64,"text":953},{"id":1005,"depth":64,"text":1006},{"id":1044,"depth":64,"text":1045},{"id":1068,"depth":64,"text":1069},{"id":1113,"depth":46,"text":1114},{"id":1186,"depth":46,"text":1187},{"id":1217,"depth":46,"text":1218},{"id":1269,"depth":46,"text":1270},"Complete guide on flask static vs media files explained for Flask production environments.","md",{"ogTitle":5,"ogDescription":1297,"twitterCard":1300,"robots":1301,"canonical":1302},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Freference\u002Fflask-static-vs-media-files-explained","\u002Freference\u002Fflask-static-vs-media-files-explained",{"title":5,"description":1297},"reference\u002Fflask-static-vs-media-files-explained","Ly68TTss0jpvFKv99mJURgIvedRTsIalPSAML9D0AXU",1776805765080]