Ravindra BagaleCourses & study guides मराठी Track your progress

Chapter 11: Application Hosting — WordPress, Flask and Express

Common Mistakes I See Students Make

Friends, I see these mistakes in every batch – WordPress without an Elastic IP, wrong ownership, debug=True in production. Read them once.

  • Installing WordPress before attaching an Elastic IP — the site breaks after stop/start.
  • Wrong ownership of WordPress files (not www-data/apache) → plugins and uploads fail.
  • Forgetting AllowOverride All / a2enmod rewrite → permalinks give 404.
  • Skipping the security salts in wp-config.php.
  • Running Flask with app.run(debug=True) in production — debug mode can allow remote code execution.
  • Wrong module path in Gunicorn (app:app vs wsgi:app) → ModuleNotFoundError in journalctl.
  • Not running the command printed by pm2 startup, so apps don't come back after reboot.

Each mistake, what you see on screen, and the fix

These are the problems I see most often when students host WordPress, Flask and Express on EC2. For each one: what breaks, what you see, and how to fix it.

Request path: browser to Elastic IP to web server to app server Browser Elastic IP Nginx / Apache PHP-FPM / Gunicorn / PM2
Most hosting mistakes break one box in this chain. Find the box, and you have found the fix.

1. WordPress installed before attaching an Elastic IP

WordPress saves the site address in its database (siteurl and home). A normal EC2 public IP changes when you stop and start the instance. After a restart, the site redirects to the old IP and looks broken.

What you see: the browser spins, then tries to open the old IP address.

Fix:

  1. Allocate an Elastic IP and associate it with the instance.
  2. Update the two values, using WP-CLI if it is installed: wp option update home 'http://NEW_IP' and wp option update siteurl 'http://NEW_IP'.
  3. Better still, use a domain name from day one.

2. Wrong file ownership

The web server user must be able to write to wp-content. On Ubuntu this user is www-data. On Amazon Linux with Apache it is apache.

What you see: "Installation failed: Could not create directory" when adding a plugin.

Fix (Ubuntu): sudo chown -R www-data:www-data /var/www/html/wp-content. Never use chmod 777. It lets anyone on the server change your files.

3. Permalinks give 404

Pretty URLs like /blog/my-post/ need Apache's rewrite module and permission to read .htaccess.

Fix (Ubuntu): sudo a2enmod rewrite, set AllowOverride All for the site directory, then sudo systemctl restart apache2.

4. Skipping the security salts

wp-config.php has keys and salts that protect login cookies. Leaving the sample placeholder values in place weakens them. Generate fresh values from the official WordPress salt generator, paste them in, and keep the file out of any public Git repo.

5. Flask with debug=True in production

Flask's debugger shows an interactive console in the browser when an error happens. If it is reachable from the internet, an attacker may be able to run code on your server. This is a real, documented risk.

Global example: the Flask documentation itself warns never to use the debugger on a production server.

Fix: run the app with Gunicorn behind Nginx, with debug off.

6. Wrong Gunicorn module path

If your file is app.py and the Flask object is named app, the target is app:app. If it lives in wsgi.py, the target is wsgi:app.

What you see: sudo journalctl -u myapp shows ModuleNotFoundError, and Nginx returns 502 Bad Gateway.

7. PM2 apps do not come back after reboot

pm2 startup prints a command that you must copy and run with sudo. Then run pm2 save. Without both steps, your Express app stays down after a restart.

Indian-company scenario

Suppose we are a junior cloud engineer at a Bengaluru startup hosting a Flask API for a food-delivery partner portal. This is sample data. After a weekend reboot, monitoring shows:

curl -I https://api.example.in  → HTTP/1.1 502 Bad Gateway

Checklist: Is Gunicorn running? (systemctl status) Is it enabled at boot? (systemctl enable) Is the socket path in Nginx the same as Gunicorn's? Two minutes with this checklist beats an hour of guessing.

Everyday analogy

A restaurant (Nginx) takes your order and passes it to the kitchen (Gunicorn/PHP-FPM/PM2). If the kitchen is closed, the waiter can only say "sorry". That is the 502 error. The front door is open, but nobody is cooking.

The same mistakes in other stacks

The three apps in this chapter are WordPress (PHP), Flask (Python) and Express (Node.js). The same kinds of mistakes appear in other stacks too.

Java / Spring Boot on Tomcat or as a JAR

Mistake: running the app as root on port 80. Fix: run as a normal user on 8080 behind nginx, using a systemd service.

[Service]
User=appuser
ExecStart=/usr/bin/java -jar /opt/app/app.jar

What you see:

systemctl status app -> active (running)
C# / ASP.NET Core

Mistake: ASPNETCORE_ENVIRONMENT=Development in production, which shows detailed error pages. Fix: set Production.

Environment=ASPNETCORE_ENVIRONMENT=Production

What you see:

Errors show a generic page, not a stack trace
Python / Django

Mistake: DEBUG=True and an empty ALLOWED_HOSTS. Fix: DEBUG off and list your domain.

ALLOWED_HOSTS = ['example.in']

What you see:

Invalid HTTP_HOST header -> 400 for unknown hosts
Go

Mistake: building on a laptop with a different OS or CPU. Fix: cross-compile with the right flags.

GOOS=linux GOARCH=amd64 go build -o app

What you see:

exec format error  (what you see when you skip this)
PHP / Laravel

Mistake: the web root points to the project folder instead of public/, which can expose .env. Fix: set root to public.

root /var/www/app/public;

What you see:

GET /.env -> 404
Ruby / Rails

Mistake: forgetting to precompile assets, so CSS is missing. Fix: precompile during deploy.

RAILS_ENV=production bin/rails assets:precompile

What you see:

Writing public/assets/application-*.css
nginx in front of any app

Mistake: proxy_pass to the wrong port. Fix: match it to the app's port (8080, 3000, 5000...).

proxy_pass http://127.0.0.1:8000;

What you see:

502 Bad Gateway when wrong
Databases: MySQL, PostgreSQL, MongoDB, Redis

Mistake: an app config pointing to localhost after moving to RDS, Atlas or ElastiCache. Fix: use the managed endpoint stored in an environment variable or a secrets manager.

What you see:

Connection refused 127.0.0.1:3306  (before the fix)
AWS / Azure / GCP managed hosting

Elastic Beanstalk, Azure App Service and Google App Engine or Cloud Run take care of the process manager for you. The usual mistake is a wrong start command or port. Read the platform logs first.

What you see:

Container failed to start and listen on the port defined by PORT=8080

Summary

StackClassic mistakeFix
Spring BootRun as root on :80User service on :8080 + nginx
ASP.NET CoreDevelopment env in prodProduction env
DjangoDEBUG TrueDEBUG False + ALLOWED_HOSTS
GoWrong OS/arch buildGOOS/GOARCH
LaravelRoot not public/root .../public
RailsNo asset precompileassets:precompile
nginxWrong proxy portMatch app port
Databaseslocalhost after migrationManaged endpoint via env

Recap

  • Elastic IP or a domain before WordPress. Correct owner, never 777.
  • Turn on rewrite for permalinks, and set fresh salts.
  • No Flask debug in production. Check the Gunicorn module path.
  • PM2: run the startup command, then pm2 save.