Depending on the size of the thumbnail your really only talking about a couple of mbs per thumbnail.
First and foremost you need to set caching headers properly and probably for a longer length. By default each page load sends a request for each file (CSS, js images etc.) in production this isn't needed generally. So even though the files are cached it still double checks every request.
Extending cache times, using versioning and setting headers will help tremendously.
When I have images that come from users, I always set a max size (dimensions) and thumbnail requirements. Can be dynamic if needed in a setting file or table.
I upload the original to the server, fire off an event. This event will have at least two listeners, usually three.
I like intervention image package, but there are other software and plugins. Each listener is responsible for resizing the original. Last listener uploads the resized to hosting (if applicable, like googles cdn or s3) and deletes the other images on the server.
I set a default image on upload to a image that says image is processing. Obviously the listener updates the path in the db after upload to final spot/resized.
Now I generally set one image to max size. Say header image on a blog. Then a mobile size for small screens, and a thumbnail (100x100 etc). The post or articles table stores the paths in the applicable column.
As a side note, if your using separate hosting, that's why I upload to the server. Manipulate the image then upload. Less issues and network lag. Also, now you see why YouTube lost millions just hosting the billions of tarabits of videos :)