I built my video downloader in Node.js first. It worked on my laptop, it worked in my tests, and it fell over the moment I tried to put it on the hosting I had already paid for. My Hostinger Premium plan does not run Node.js at all. There is no process manager, no long-running server, no port I can listen on. What it does run is PHP, so I had a choice: pay for different hosting, or rebuild the whole thing in plain PHP. I rebuilt it.
The first thing that changed was the mental model. A Node app is a process that stays alive and waits for requests. PHP on shared hosting is the opposite: a script starts, handles one request, and dies. There is no memory between requests unless you write it to a file or a database. So everything that used to live in memory — the rate limiter, the little cache of recent provider responses, the retry state — had to be rethought.
The rate limiter was the most instructive part. In Node I kept a map of IP addresses to timestamps in memory and trimmed it on every request. In PHP there is no map. I replaced it with a small directory of counter files, one per IP, each holding a count and a window start time. A request reads its file, checks the count, increments it, and writes it back. It is slower than memory and it needs a file lock so two simultaneous requests do not clobber each other, but it caps abusive clients at 30 requests a minute with nothing more than flock() and a text file. No Redis, no database, no daemon.
The provider calls changed shape too. Node's fetch and async/await made the flow easy to read: call the API, wait, parse, return. PHP's curl is synchronous, so a slow provider call blocks the whole request. That forced me to be honest about timeouts. Every provider call now gets a short connect timeout and a total timeout, because one slow API cannot be allowed to hang the whole page. When a provider says "Service is busy", the code retries up to three times with a short pause between attempts, then gives up and tells the visitor plainly. The retry loop is boring code. It is also the reason the site stays up on bad afternoons.
Downloads were the hardest rewrite. In Node I could pipe a remote stream straight through to the visitor, a few kilobytes at a time. PHP can do the same with fopen and fpassthru, but only if you are careful: read in small chunks, flush as you go, and never load the whole file into memory. A shared host with a 256 MB memory limit will kill a script that tries to hold a 400 MB video. Streaming in chunks keeps memory flat no matter how big the file is, and the visitor still gets the same file.
There was one thing I deliberately did not rebuild: storing files. The original Node version never saved a downloaded video to disk, and the PHP version does not either. The file passes through the server and is gone. That is partly a disk-space decision — shared hosting gives you very little — and partly a privacy one. There is nothing to clean up later if nothing was ever written.
The part I expected to hate was the file structure. Node projects have package.json, a build step, and dependencies that need installing. The PHP version has none of that. The project is a folder of .php files, some HTML pages, and a .htaccess file that blocks access to the config file holding the API key. Deploying is copying files over SFTP. There is no build to break and no node_modules folder to upload. For a small tool, that is a genuine advantage, not just a compromise.
I lost some things in the move. The developer experience is worse, the error messages are less helpful, and I miss having a proper package manager. But I gained something I did not expect: the site got simpler. Every feature had to justify its existence in a language with no framework and no background process. The things that survived — resolve the link, proxy the file, show an honest error — are the things the site actually is.
The rebuild went live on ramrom.io and it has run there ever since. If your hosting plan decides what your stack can be, that is not a reason to pay for a bigger plan. Sometimes it is a reason to write a smaller app.
