[{"data":1,"prerenderedAt":1399},["ShallowReactive",2],{"\u002Fdeploy\u002Fhow-to-set-up-https-for-flask-nginx-plus-lets-encrypt":3},{"id":4,"title":5,"body":6,"description":1389,"extension":1390,"meta":1391,"navigation":360,"path":1395,"seo":1396,"stem":1397,"__hash__":1398},"content\u002Fdeploy\u002Fhow-to-set-up-https-for-flask-nginx-plus-lets-encrypt.md","How to Set Up HTTPS for Flask (Nginx + Let’s Encrypt)",{"type":7,"value":8,"toc":1378},"minimark",[9,13,17,22,169,187,191,201,205,787,791,910,914,917,1111,1114,1182,1189,1193,1278,1285,1289,1307,1311,1319,1330,1342,1350,1364,1368,1374],[10,11,5],"h1",{"id":12},"how-to-set-up-https-for-flask-nginx-lets-encrypt",[14,15,16],"p",{},"If you're trying to enable HTTPS for a Flask app in production, this guide shows you how to configure Nginx with Let's Encrypt step-by-step. The result is a valid TLS certificate, automatic HTTP-to-HTTPS redirect, and a renewable production setup.",[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","sudo apt update\nsudo apt install -y certbot python3-certbot-nginx\nsudo nginx -t && sudo systemctl reload nginx\nsudo certbot --nginx -d example.com -d www.example.com\nsudo systemctl status certbot.timer\nsudo nginx -t && sudo systemctl reload nginx\ncurl -I http:\u002F\u002Fexample.com\ncurl -I https:\u002F\u002Fexample.com\n","bash","",[30,31,32,48,68,94,115,128,147,159],"code",{"__ignoreMap":28},[33,34,37,41,45],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sScJk","sudo",[33,42,44],{"class":43},"sZZnC"," apt",[33,46,47],{"class":43}," update\n",[33,49,51,53,55,58,62,65],{"class":35,"line":50},2,[33,52,40],{"class":39},[33,54,44],{"class":43},[33,56,57],{"class":43}," install",[33,59,61],{"class":60},"sj4cs"," -y",[33,63,64],{"class":43}," certbot",[33,66,67],{"class":43}," python3-certbot-nginx\n",[33,69,71,73,76,79,83,85,88,91],{"class":35,"line":70},3,[33,72,40],{"class":39},[33,74,75],{"class":43}," nginx",[33,77,78],{"class":60}," -t",[33,80,82],{"class":81},"sVt8B"," && ",[33,84,40],{"class":39},[33,86,87],{"class":43}," systemctl",[33,89,90],{"class":43}," reload",[33,92,93],{"class":43}," nginx\n",[33,95,97,99,101,104,107,110,112],{"class":35,"line":96},4,[33,98,40],{"class":39},[33,100,64],{"class":43},[33,102,103],{"class":60}," --nginx",[33,105,106],{"class":60}," -d",[33,108,109],{"class":43}," example.com",[33,111,106],{"class":60},[33,113,114],{"class":43}," www.example.com\n",[33,116,118,120,122,125],{"class":35,"line":117},5,[33,119,40],{"class":39},[33,121,87],{"class":43},[33,123,124],{"class":43}," status",[33,126,127],{"class":43}," certbot.timer\n",[33,129,131,133,135,137,139,141,143,145],{"class":35,"line":130},6,[33,132,40],{"class":39},[33,134,75],{"class":43},[33,136,78],{"class":60},[33,138,82],{"class":81},[33,140,40],{"class":39},[33,142,87],{"class":43},[33,144,90],{"class":43},[33,146,93],{"class":43},[33,148,150,153,156],{"class":35,"line":149},7,[33,151,152],{"class":39},"curl",[33,154,155],{"class":60}," -I",[33,157,158],{"class":43}," http:\u002F\u002Fexample.com\n",[33,160,162,164,166],{"class":35,"line":161},8,[33,163,152],{"class":39},[33,165,155],{"class":60},[33,167,168],{"class":43}," https:\u002F\u002Fexample.com\n",[14,170,171,172,175,176,179,180,175,183,186],{},"Replace ",[30,173,174],{},"example.com"," and ",[30,177,178],{},"www.example.com"," with your real domain. This works when DNS already points to the server, Nginx serves the domain correctly on port 80, and ports ",[30,181,182],{},"80",[30,184,185],{},"443"," are open.",[18,188,190],{"id":189},"whats-happening","What’s Happening",[14,192,193,194,196,197,200],{},"HTTPS for Flask is usually terminated at Nginx, not inside Flask or Gunicorn. Let’s Encrypt issues certificates only after validating domain ownership, typically over HTTP on port ",[30,195,182],{},". Certbot updates the Nginx server block to use the certificate files and can add the HTTP-to-HTTPS redirect automatically. If DNS, firewall rules, or ",[30,198,199],{},"server_name"," are wrong, issuance and renewal fail.",[18,202,204],{"id":203},"step-by-step-guide","Step-by-Step Guide",[206,207,208,246,289,323,426,473,518,547,630,667,706,767],"ol",{},[209,210,211,215,218,219,243,245],"li",{},[212,213,214],"strong",{},"Confirm the domain points to the server",[216,217],"br",{},"Run:",[23,220,222],{"className":25,"code":221,"language":27,"meta":28,"style":28},"dig +short example.com\ndig +short www.example.com\n",[30,223,224,235],{"__ignoreMap":28},[33,225,226,229,232],{"class":35,"line":36},[33,227,228],{"class":39},"dig",[33,230,231],{"class":43}," +short",[33,233,234],{"class":43}," example.com\n",[33,236,237,239,241],{"class":35,"line":50},[33,238,228],{"class":39},[33,240,231],{"class":43},[33,242,114],{"class":43},[216,244],{},"Verify the returned IP matches your server’s public IP.",[209,247,248,251,253,254,281,283,284,175,286,288],{},[212,249,250],{},"Open required firewall ports",[216,252],{},"If using UFW:",[23,255,257],{"className":25,"code":256,"language":27,"meta":28,"style":28},"sudo ufw allow 80,443\u002Ftcp\nsudo ufw status\n",[30,258,259,272],{"__ignoreMap":28},[33,260,261,263,266,269],{"class":35,"line":36},[33,262,40],{"class":39},[33,264,265],{"class":43}," ufw",[33,267,268],{"class":43}," allow",[33,270,271],{"class":43}," 80,443\u002Ftcp\n",[33,273,274,276,278],{"class":35,"line":50},[33,275,40],{"class":39},[33,277,265],{"class":43},[33,279,280],{"class":43}," status\n",[216,282],{},"If using a cloud firewall or security group, also allow inbound TCP ",[30,285,182],{},[30,287,185],{}," there.",[209,290,291,294],{},[212,292,293],{},"Install Nginx and Certbot",[23,295,297],{"className":25,"code":296,"language":27,"meta":28,"style":28},"sudo apt update\nsudo apt install -y nginx certbot python3-certbot-nginx\n",[30,298,299,307],{"__ignoreMap":28},[33,300,301,303,305],{"class":35,"line":36},[33,302,40],{"class":39},[33,304,44],{"class":43},[33,306,47],{"class":43},[33,308,309,311,313,315,317,319,321],{"class":35,"line":50},[33,310,40],{"class":39},[33,312,44],{"class":43},[33,314,57],{"class":43},[33,316,61],{"class":60},[33,318,75],{"class":43},[33,320,64],{"class":43},[33,322,67],{"class":43},[209,324,325,328,330,331,334,412,414,415,420,421,425],{},[212,326,327],{},"Create or verify the Nginx server block for HTTP",[216,329],{},"Example file: ",[30,332,333],{},"\u002Fetc\u002Fnginx\u002Fsites-available\u002Fflaskapp",[23,335,339],{"className":336,"code":337,"language":338,"meta":28,"style":28},"language-nginx shiki shiki-themes github-light github-dark","server {\n    listen 80;\n    server_name example.com www.example.com;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:8000;\n        include proxy_params;\n        proxy_redirect off;\n        proxy_set_header Host $host;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    }\n}\n","nginx",[30,340,341,346,351,356,362,367,372,377,382,388,394,400,406],{"__ignoreMap":28},[33,342,343],{"class":35,"line":36},[33,344,345],{},"server {\n",[33,347,348],{"class":35,"line":50},[33,349,350],{},"    listen 80;\n",[33,352,353],{"class":35,"line":70},[33,354,355],{},"    server_name example.com www.example.com;\n",[33,357,358],{"class":35,"line":96},[33,359,361],{"emptyLinePlaceholder":360},true,"\n",[33,363,364],{"class":35,"line":117},[33,365,366],{},"    location \u002F {\n",[33,368,369],{"class":35,"line":130},[33,370,371],{},"        proxy_pass http:\u002F\u002F127.0.0.1:8000;\n",[33,373,374],{"class":35,"line":149},[33,375,376],{},"        include proxy_params;\n",[33,378,379],{"class":35,"line":161},[33,380,381],{},"        proxy_redirect off;\n",[33,383,385],{"class":35,"line":384},9,[33,386,387],{},"        proxy_set_header Host $host;\n",[33,389,391],{"class":35,"line":390},10,[33,392,393],{},"        proxy_set_header X-Forwarded-Proto $scheme;\n",[33,395,397],{"class":35,"line":396},11,[33,398,399],{},"        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n",[33,401,403],{"class":35,"line":402},12,[33,404,405],{},"    }\n",[33,407,409],{"class":35,"line":408},13,[33,410,411],{},"}\n",[216,413],{},"If your Flask app is served through Gunicorn, make sure Gunicorn is already working first. If not, use ",[416,417,419],"a",{"href":418},"\u002Fdeploy\u002Fdeploy-flask-with-nginx-plus-gunicorn-step-by-step-guide","Deploy Flask with Nginx + Gunicorn (Step-by-Step Guide)"," or ",[416,422,424],{"href":423},"\u002Fdeploy\u002Fdeploy-flask-on-ubuntu-vps-step-by-step","Deploy Flask on Ubuntu VPS (Step-by-Step)",".",[209,427,428,431,452,454,455],{},[212,429,430],{},"Enable the site",[23,432,434],{"className":25,"code":433,"language":27,"meta":28,"style":28},"sudo ln -s \u002Fetc\u002Fnginx\u002Fsites-available\u002Fflaskapp \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F\n",[30,435,436],{"__ignoreMap":28},[33,437,438,440,443,446,449],{"class":35,"line":36},[33,439,40],{"class":39},[33,441,442],{"class":43}," ln",[33,444,445],{"class":60}," -s",[33,447,448],{"class":43}," \u002Fetc\u002Fnginx\u002Fsites-available\u002Fflaskapp",[33,450,451],{"class":43}," \u002Fetc\u002Fnginx\u002Fsites-enabled\u002F\n",[216,453],{},"If the default site conflicts, remove it:",[23,456,458],{"className":25,"code":457,"language":27,"meta":28,"style":28},"sudo rm -f \u002Fetc\u002Fnginx\u002Fsites-enabled\u002Fdefault\n",[30,459,460],{"__ignoreMap":28},[33,461,462,464,467,470],{"class":35,"line":36},[33,463,40],{"class":39},[33,465,466],{"class":43}," rm",[33,468,469],{"class":60}," -f",[33,471,472],{"class":43}," \u002Fetc\u002Fnginx\u002Fsites-enabled\u002Fdefault\n",[209,474,475,478,502,504,505],{},[212,476,477],{},"Validate and reload Nginx",[23,479,481],{"className":25,"code":480,"language":27,"meta":28,"style":28},"sudo nginx -t\nsudo systemctl reload nginx\n",[30,482,483,492],{"__ignoreMap":28},[33,484,485,487,489],{"class":35,"line":36},[33,486,40],{"class":39},[33,488,75],{"class":43},[33,490,491],{"class":60}," -t\n",[33,493,494,496,498,500],{"class":35,"line":50},[33,495,40],{"class":39},[33,497,87],{"class":43},[33,499,90],{"class":43},[33,501,93],{"class":43},[216,503],{},"Test plain HTTP before requesting a certificate:",[23,506,508],{"className":25,"code":507,"language":27,"meta":28,"style":28},"curl -I http:\u002F\u002Fexample.com\n",[30,509,510],{"__ignoreMap":28},[33,511,512,514,516],{"class":35,"line":36},[33,513,152],{"class":39},[33,515,155],{"class":60},[33,517,158],{"class":43},[209,519,520,523,544,546],{},[212,521,522],{},"Request and install the certificate with Certbot",[23,524,526],{"className":25,"code":525,"language":27,"meta":28,"style":28},"sudo certbot --nginx -d example.com -d www.example.com\n",[30,527,528],{"__ignoreMap":28},[33,529,530,532,534,536,538,540,542],{"class":35,"line":36},[33,531,40],{"class":39},[33,533,64],{"class":43},[33,535,103],{"class":60},[33,537,106],{"class":60},[33,539,109],{"class":43},[33,541,106],{"class":60},[33,543,114],{"class":43},[216,545],{},"Choose the redirect option when prompted.",[209,548,549,552,554,555],{},[212,550,551],{},"Verify the generated TLS configuration",[216,553],{},"Certbot should update your Nginx config to include a TLS server block similar to:",[23,556,558],{"className":336,"code":557,"language":338,"meta":28,"style":28},"server {\n    listen 443 ssl;\n    server_name example.com www.example.com;\n\n    ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Ffullchain.pem;\n    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Fprivkey.pem;\n\n    location \u002F {\n        proxy_pass http:\u002F\u002F127.0.0.1:8000;\n        include proxy_params;\n        proxy_redirect off;\n        proxy_set_header Host $host;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    }\n}\n",[30,559,560,564,569,573,577,582,587,591,595,599,603,607,611,615,620,625],{"__ignoreMap":28},[33,561,562],{"class":35,"line":36},[33,563,345],{},[33,565,566],{"class":35,"line":50},[33,567,568],{},"    listen 443 ssl;\n",[33,570,571],{"class":35,"line":70},[33,572,355],{},[33,574,575],{"class":35,"line":96},[33,576,361],{"emptyLinePlaceholder":360},[33,578,579],{"class":35,"line":117},[33,580,581],{},"    ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Ffullchain.pem;\n",[33,583,584],{"class":35,"line":130},[33,585,586],{},"    ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fexample.com\u002Fprivkey.pem;\n",[33,588,589],{"class":35,"line":149},[33,590,361],{"emptyLinePlaceholder":360},[33,592,593],{"class":35,"line":161},[33,594,366],{},[33,596,597],{"class":35,"line":384},[33,598,371],{},[33,600,601],{"class":35,"line":390},[33,602,376],{},[33,604,605],{"class":35,"line":396},[33,606,381],{},[33,608,609],{"class":35,"line":402},[33,610,387],{},[33,612,613],{"class":35,"line":408},[33,614,393],{},[33,616,618],{"class":35,"line":617},14,[33,619,399],{},[33,621,623],{"class":35,"line":622},15,[33,624,405],{},[33,626,628],{"class":35,"line":627},16,[33,629,411],{},[209,631,632,635,637,638,640,641],{},[212,633,634],{},"Force HTTPS if Certbot did not add the redirect",[216,636],{},"Use a dedicated port ",[30,639,182],{}," redirect block:",[23,642,644],{"className":336,"code":643,"language":338,"meta":28,"style":28},"server {\n    listen 80;\n    server_name example.com www.example.com;\n    return 301 https:\u002F\u002F$host$request_uri;\n}\n",[30,645,646,650,654,658,663],{"__ignoreMap":28},[33,647,648],{"class":35,"line":36},[33,649,345],{},[33,651,652],{"class":35,"line":50},[33,653,350],{},[33,655,656],{"class":35,"line":70},[33,657,355],{},[33,659,660],{"class":35,"line":96},[33,661,662],{},"    return 301 https:\u002F\u002F$host$request_uri;\n",[33,664,665],{"class":35,"line":117},[33,666,411],{},[209,668,669,672,703,705],{},[212,670,671],{},"Reload and test HTTPS",[23,673,675],{"className":25,"code":674,"language":27,"meta":28,"style":28},"sudo nginx -t\nsudo systemctl reload nginx\ncurl -I https:\u002F\u002Fexample.com\n",[30,676,677,685,695],{"__ignoreMap":28},[33,678,679,681,683],{"class":35,"line":36},[33,680,40],{"class":39},[33,682,75],{"class":43},[33,684,491],{"class":60},[33,686,687,689,691,693],{"class":35,"line":50},[33,688,40],{"class":39},[33,690,87],{"class":43},[33,692,90],{"class":43},[33,694,93],{"class":43},[33,696,697,699,701],{"class":35,"line":70},[33,698,152],{"class":39},[33,700,155],{"class":60},[33,702,168],{"class":43},[216,704],{},"Confirm in a browser that the certificate is valid and trusted.",[209,707,708,711,713,714,747,749,750],{},[212,709,710],{},"Verify automatic renewal",[216,712],{},"Check the systemd timer:",[23,715,717],{"className":25,"code":716,"language":27,"meta":28,"style":28},"systemctl list-timers | grep certbot\nsudo systemctl status certbot.timer\n",[30,718,719,737],{"__ignoreMap":28},[33,720,721,724,727,731,734],{"class":35,"line":36},[33,722,723],{"class":39},"systemctl",[33,725,726],{"class":43}," list-timers",[33,728,730],{"class":729},"szBVR"," |",[33,732,733],{"class":39}," grep",[33,735,736],{"class":43}," certbot\n",[33,738,739,741,743,745],{"class":35,"line":50},[33,740,40],{"class":39},[33,742,87],{"class":43},[33,744,124],{"class":43},[33,746,127],{"class":43},[216,748],{},"Test renewal without making changes:",[23,751,753],{"className":25,"code":752,"language":27,"meta":28,"style":28},"sudo certbot renew --dry-run\n",[30,754,755],{"__ignoreMap":28},[33,756,757,759,761,764],{"class":35,"line":36},[33,758,40],{"class":39},[33,760,64],{"class":43},[33,762,763],{"class":43}," renew",[33,765,766],{"class":60}," --dry-run\n",[209,768,769,772,774,775,784,786],{},[212,770,771],{},"Ensure Flask trusts the forwarded HTTPS scheme",[216,773],{},"If Flask generates wrong redirect URLs or enters redirect loops, make sure Nginx sends:",[23,776,778],{"className":336,"code":777,"language":338,"meta":28,"style":28},"proxy_set_header X-Forwarded-Proto $scheme;\n",[30,779,780],{"__ignoreMap":28},[33,781,782],{"class":35,"line":36},[33,783,777],{},[216,785],{},"If needed, configure proxy header handling in your Flask or WSGI stack so the app correctly sees the original request scheme as HTTPS.",[18,788,790],{"id":789},"common-causes","Common Causes",[792,793,794,800,813,822,830,839,849,859,868,878,891,897],"ul",{},[209,795,796,799],{},[212,797,798],{},"DNS not pointing to the correct server IP"," → Let’s Encrypt validation reaches the wrong host or times out → Update A\u002FAAAA records and wait for propagation.",[209,801,802,808,809,812],{},[212,803,804,805,807],{},"Nginx ",[30,806,199],{}," mismatch"," → Certbot modifies or validates the wrong server block → Set ",[30,810,811],{},"server_name example.com www.example.com;"," on the intended site and reload Nginx.",[209,814,815,818,819,821],{},[212,816,817],{},"Port 80 blocked by firewall"," → HTTP challenge fails before certificate issuance → Open inbound TCP ",[30,820,182],{}," at the OS and cloud firewall.",[209,823,824,827,828,425],{},[212,825,826],{},"Port 443 blocked"," → HTTPS works internally but is unreachable externally → Open inbound TCP ",[30,829,185],{},[209,831,832,835,836,425],{},[212,833,834],{},"Default Nginx site taking precedence"," → Requests hit the wrong server block → Remove or disable conflicting configs and confirm with ",[30,837,838],{},"nginx -T",[209,840,841,844,845,848],{},[212,842,843],{},"Invalid Nginx config"," → Certbot cannot install certificate changes safely → Fix syntax errors, test with ",[30,846,847],{},"nginx -t",", then rerun Certbot.",[209,850,851,854,855,858],{},[212,852,853],{},"Wrong certificate paths or stale config"," → Nginx serves an old or missing certificate → Verify ",[30,856,857],{},"\u002Fetc\u002Fletsencrypt\u002Flive\u002F\u003Cdomain>\u002F"," paths and reload Nginx.",[209,860,861,864,865,867],{},[212,862,863],{},"No HTTP-to-HTTPS redirect"," → Site loads on both protocols or remains on HTTP → Add a port ",[30,866,182],{}," redirect block or use the Certbot redirect option.",[209,869,870,873,874,877],{},[212,871,872],{},"Proxy headers missing"," → Flask generates insecure URLs or redirect loops behind HTTPS → Send ",[30,875,876],{},"X-Forwarded-Proto"," and configure proxy middleware correctly.",[209,879,880,883,884,887,888,425],{},[212,881,882],{},"Renewal not configured or failing"," → Certificate expires after initial setup → Verify ",[30,885,886],{},"certbot.timer"," and run ",[30,889,890],{},"certbot renew --dry-run",[209,892,893,896],{},[212,894,895],{},"AAAA record points to a different server"," → Validation or browser traffic uses IPv6 and fails unexpectedly → Fix or remove the incorrect AAAA record.",[209,898,899,902,903,905,906,909],{},[212,900,901],{},"Certbot challenge path intercepted by custom routing or redirect logic"," → ACME validation fails → Simplify port ",[30,904,182],{}," handling during issuance and confirm ",[30,907,908],{},"\u002F.well-known\u002Facme-challenge\u002F"," is reachable.",[18,911,913],{"id":912},"debugging-section","Debugging Section",[14,915,916],{},"Check the following commands and outputs:",[23,918,920],{"className":25,"code":919,"language":27,"meta":28,"style":28},"dig +short example.com\ndig +short www.example.com\ncurl -I http:\u002F\u002Fexample.com\ncurl -I https:\u002F\u002Fexample.com\nsudo nginx -t\nsudo nginx -T | less\nsudo systemctl status nginx\nsudo ss -tulpn | grep -E ':80|:443'\nsudo tail -f \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\nsudo tail -f \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\nsudo certbot certificates\nsudo certbot --nginx -d example.com -d www.example.com\nsudo certbot renew --dry-run\nsudo systemctl status certbot.timer\njournalctl -u nginx --no-pager -n 100\nsudo grep -R \"server_name\\|ssl_certificate\\|listen 443\" \u002Fetc\u002Fnginx\u002Fsites-enabled \u002Fetc\u002Fnginx\u002Fconf.d\n",[30,921,922,930,938,946,954,962,976,986,1006,1018,1029,1038,1054,1064,1074,1093],{"__ignoreMap":28},[33,923,924,926,928],{"class":35,"line":36},[33,925,228],{"class":39},[33,927,231],{"class":43},[33,929,234],{"class":43},[33,931,932,934,936],{"class":35,"line":50},[33,933,228],{"class":39},[33,935,231],{"class":43},[33,937,114],{"class":43},[33,939,940,942,944],{"class":35,"line":70},[33,941,152],{"class":39},[33,943,155],{"class":60},[33,945,158],{"class":43},[33,947,948,950,952],{"class":35,"line":96},[33,949,152],{"class":39},[33,951,155],{"class":60},[33,953,168],{"class":43},[33,955,956,958,960],{"class":35,"line":117},[33,957,40],{"class":39},[33,959,75],{"class":43},[33,961,491],{"class":60},[33,963,964,966,968,971,973],{"class":35,"line":130},[33,965,40],{"class":39},[33,967,75],{"class":43},[33,969,970],{"class":60}," -T",[33,972,730],{"class":729},[33,974,975],{"class":39}," less\n",[33,977,978,980,982,984],{"class":35,"line":149},[33,979,40],{"class":39},[33,981,87],{"class":43},[33,983,124],{"class":43},[33,985,93],{"class":43},[33,987,988,990,993,996,998,1000,1003],{"class":35,"line":161},[33,989,40],{"class":39},[33,991,992],{"class":43}," ss",[33,994,995],{"class":60}," -tulpn",[33,997,730],{"class":729},[33,999,733],{"class":39},[33,1001,1002],{"class":60}," -E",[33,1004,1005],{"class":43}," ':80|:443'\n",[33,1007,1008,1010,1013,1015],{"class":35,"line":384},[33,1009,40],{"class":39},[33,1011,1012],{"class":43}," tail",[33,1014,469],{"class":60},[33,1016,1017],{"class":43}," \u002Fvar\u002Flog\u002Fnginx\u002Ferror.log\n",[33,1019,1020,1022,1024,1026],{"class":35,"line":390},[33,1021,40],{"class":39},[33,1023,1012],{"class":43},[33,1025,469],{"class":60},[33,1027,1028],{"class":43}," \u002Fvar\u002Flog\u002Fnginx\u002Faccess.log\n",[33,1030,1031,1033,1035],{"class":35,"line":396},[33,1032,40],{"class":39},[33,1034,64],{"class":43},[33,1036,1037],{"class":43}," certificates\n",[33,1039,1040,1042,1044,1046,1048,1050,1052],{"class":35,"line":402},[33,1041,40],{"class":39},[33,1043,64],{"class":43},[33,1045,103],{"class":60},[33,1047,106],{"class":60},[33,1049,109],{"class":43},[33,1051,106],{"class":60},[33,1053,114],{"class":43},[33,1055,1056,1058,1060,1062],{"class":35,"line":408},[33,1057,40],{"class":39},[33,1059,64],{"class":43},[33,1061,763],{"class":43},[33,1063,766],{"class":60},[33,1065,1066,1068,1070,1072],{"class":35,"line":617},[33,1067,40],{"class":39},[33,1069,87],{"class":43},[33,1071,124],{"class":43},[33,1073,127],{"class":43},[33,1075,1076,1079,1082,1084,1087,1090],{"class":35,"line":622},[33,1077,1078],{"class":39},"journalctl",[33,1080,1081],{"class":60}," -u",[33,1083,75],{"class":43},[33,1085,1086],{"class":60}," --no-pager",[33,1088,1089],{"class":60}," -n",[33,1091,1092],{"class":60}," 100\n",[33,1094,1095,1097,1099,1102,1105,1108],{"class":35,"line":627},[33,1096,40],{"class":39},[33,1098,733],{"class":43},[33,1100,1101],{"class":60}," -R",[33,1103,1104],{"class":43}," \"server_name\\|ssl_certificate\\|listen 443\"",[33,1106,1107],{"class":43}," \u002Fetc\u002Fnginx\u002Fsites-enabled",[33,1109,1110],{"class":43}," \u002Fetc\u002Fnginx\u002Fconf.d\n",[14,1112,1113],{},"What to look for:",[792,1115,1116,1121,1139,1145,1156,1164,1170],{},[209,1117,1118,1120],{},[30,1119,228],{}," returns the correct public IP for all hostnames.",[209,1122,1123,1126,1127,1130,1131,1134,1135,1138],{},[30,1124,1125],{},"curl -I http:\u002F\u002Fexample.com"," returns ",[30,1128,1129],{},"200",", ",[30,1132,1133],{},"301",", or ",[30,1136,1137],{},"302",", not a timeout or wrong host response.",[209,1140,1141,1144],{},[30,1142,1143],{},"curl -I https:\u002F\u002Fexample.com"," returns a valid HTTPS response.",[209,1146,1147,1149,1150,175,1153,425],{},[30,1148,847],{}," shows ",[30,1151,1152],{},"syntax is ok",[30,1154,1155],{},"test is successful",[209,1157,1158,1160,1161,1163],{},[30,1159,838],{}," shows the expected ",[30,1162,199],{},", redirect block, and certificate paths.",[209,1165,1166,1169],{},[30,1167,1168],{},"\u002Fvar\u002Flog\u002Fletsencrypt\u002Fletsencrypt.log"," contains the reason Certbot failed if issuance or renewal breaks.",[209,1171,1172,1175,1176,175,1179,425],{},[30,1173,1174],{},"ss -tulpn"," confirms Nginx is listening on ",[30,1177,1178],{},":80",[30,1180,1181],{},":443",[14,1183,1184,1185,425],{},"If HTTPS stopped working after a previous successful setup, see ",[416,1186,1188],{"href":1187},"\u002Ffix-issues\u002Fflask-https-not-working-after-certbot","Flask HTTPS Not Working After Certbot",[18,1190,1192],{"id":1191},"checklist","Checklist",[792,1194,1197,1206,1217,1226,1235,1244,1250,1256,1264,1272],{"className":1195},[1196],"contains-task-list",[209,1198,1201,1205],{"className":1199},[1200],"task-list-item",[1202,1203],"input",{"disabled":360,"type":1204},"checkbox"," Domain A\u002FAAAA records point to the correct server IP",[209,1207,1209,1211,1212,175,1214,1216],{"className":1208},[1200],[1202,1210],{"disabled":360,"type":1204}," Ports ",[30,1213,182],{},[30,1215,185],{}," are open in OS and cloud firewall rules",[209,1218,1220,1222,1223,1225],{"className":1219},[1200],[1202,1221],{"disabled":360,"type":1204}," Nginx ",[30,1224,199],{}," matches the domain exactly",[209,1227,1229,1231,1232,1234],{"className":1228},[1200],[1202,1230],{"disabled":360,"type":1204}," ",[30,1233,847],{}," passes with no errors",[209,1236,1238,1231,1240,1243],{"className":1237},[1200],[1202,1239],{"disabled":360,"type":1204},[30,1241,1242],{},"certbot --nginx"," completed successfully",[209,1245,1247,1249],{"className":1246},[1200],[1202,1248],{"disabled":360,"type":1204}," HTTP requests redirect to HTTPS",[209,1251,1253,1255],{"className":1252},[1200],[1202,1254],{"disabled":360,"type":1204}," HTTPS certificate is valid and trusted in the browser",[209,1257,1259,1231,1261,1263],{"className":1258},[1200],[1202,1260],{"disabled":360,"type":1204},[30,1262,886],{}," or renewal cron is active",[209,1265,1267,1231,1269,1271],{"className":1266},[1200],[1202,1268],{"disabled":360,"type":1204},[30,1270,890],{}," succeeds",[209,1273,1275,1277],{"className":1274},[1200],[1202,1276],{"disabled":360,"type":1204}," Flask receives correct forwarded scheme headers if required",[14,1279,1280,1281,425],{},"For a broader production validation pass, use ",[416,1282,1284],{"href":1283},"\u002Fchecklist\u002Fflask-production-checklist-everything-you-must-do","Flask Production Checklist (Everything You Must Do)",[18,1286,1288],{"id":1287},"related-guides","Related Guides",[792,1290,1291,1295,1299,1303],{},[209,1292,1293],{},[416,1294,419],{"href":418},[209,1296,1297],{},[416,1298,424],{"href":423},[209,1300,1301],{},[416,1302,1188],{"href":1187},[209,1304,1305],{},[416,1306,1284],{"href":1283},[18,1308,1310],{"id":1309},"faq","FAQ",[14,1312,1313,1316,1318],{},[212,1314,1315],{},"Q: Should I enable HTTPS in Flask itself?",[216,1317],{},"\nA: No. Use Nginx to terminate TLS and proxy to Gunicorn or the Flask app internally.",[14,1320,1321,1324,1326,1327,1329],{},[212,1322,1323],{},"Q: Why does Certbot say it cannot authenticate the domain?",[216,1325],{},"\nA: The domain likely does not resolve to this server, port ",[30,1328,182],{}," is blocked, or Nginx is serving the wrong virtual host.",[14,1331,1332,1339,1341],{},[212,1333,1334,1335,1338],{},"Q: Can I skip ",[30,1336,1337],{},"www"," and secure only the apex domain?",[216,1340],{},"\nA: Yes. Request a certificate only for the hostnames you actually serve.",[14,1343,1344,1347,1349],{},[212,1345,1346],{},"Q: Why am I getting redirect loops after enabling HTTPS?",[216,1348],{},"\nA: Nginx may be redirecting incorrectly, or Flask may not trust forwarded HTTPS headers and thinks requests are plain HTTP.",[14,1351,1352,1355,1357,1358,887,1360,1363],{},[212,1353,1354],{},"Q: How do I know renewal will work later?",[216,1356],{},"\nA: Check ",[30,1359,886],{},[30,1361,1362],{},"sudo certbot renew --dry-run"," successfully.",[18,1365,1367],{"id":1366},"final-takeaway","Final Takeaway",[14,1369,1370,1371,1373],{},"For Flask in production, HTTPS is an Nginx and certificate management task. If DNS, ",[30,1372,199],{},", port access, and Certbot renewal are all correct, HTTPS setup is straightforward and stable.",[1375,1376,1377],"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 .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 pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":28,"searchDepth":50,"depth":50,"links":1379},[1380,1381,1382,1383,1384,1385,1386,1387,1388],{"id":20,"depth":50,"text":21},{"id":189,"depth":50,"text":190},{"id":203,"depth":50,"text":204},{"id":789,"depth":50,"text":790},{"id":912,"depth":50,"text":913},{"id":1191,"depth":50,"text":1192},{"id":1287,"depth":50,"text":1288},{"id":1309,"depth":50,"text":1310},{"id":1366,"depth":50,"text":1367},"Complete guide on how to set up https for flask (nginx + let’s encrypt) for Flask production environments.","md",{"ogTitle":5,"ogDescription":1389,"twitterCard":1392,"robots":1393,"canonical":1394},"summary_large_image","index, follow","https:\u002F\u002Fflask-deployment.com\u002Fdeploy\u002Fhow-to-set-up-https-for-flask-nginx-plus-let’s-encrypt","\u002Fdeploy\u002Fhow-to-set-up-https-for-flask-nginx-plus-lets-encrypt",{"title":5,"description":1389},"deploy\u002Fhow-to-set-up-https-for-flask-nginx-plus-lets-encrypt","6slidXtP0UeJ-DSZG17-niNBpWUQivpJY2Mgj8vL2mo",1776805765064]