An upload form that accepts images is a way to put a file on your server. Validation reduces what gets through; it does not make the directory safe. The thing that makes it safe is that the web server refuses to EXECUTE anything there, whatever the file turns out to be.

Nginx

location ^~ /uploads/ {\n    location ~ \.php$ { deny all; }\n    add_header X-Content-Type-Options nosniff;\n}

Apache and LiteSpeed

# /var/www/site/uploads/.htaccess\nphp_flag engine off\n<FilesMatch "\.(php|phar|phtml|cgi|pl)$">\n    Require all denied\n</FilesMatch>\nOptions -ExecCGI -Indexes

Validate as well, properly

  • Check the real type, not the name a browser sent: finfo_file() in PHP.
  • Rename every upload to something you generated. Never keep the visitor's filename.
  • Reject double extensions - a file called invoice.php.jpg is not an accident.
  • Re-encode images. Passing a picture through an image library strips anything hidden in it.
$f = new finfo(FILEINFO_MIME_TYPE);\n$type = $f->file($tmp);\nif (!in_array($type, ['image/jpeg','image/png','image/webp'], true)) {\n    // reject\n}\n$name = bin2hex(random_bytes(16)) . '.webp';

Better still, store them somewhere the web cannot reach

Keep uploads outside the document root and serve them through a script that checks who is asking. Slower by a millisecond, and there is then no URL that can run anything.

Test it. Put a harmless PHP file in the uploads directory yourself and request it: you must get 403 or the source text, never the output. This takes a minute and it is the only proof.