The 500 errors were not in the code

A WooCommerce store of about 2,400 products loads its catalogue every day from the company’s management system. On shared hosting, 101 of 207 import calls ended in HTTP 500. The obvious reading was a bug in the import code.
Rather than start patching, I cross-referenced four logs from different layers of the stack. They agreed on something no application log would show: PHP processes were being killed from outside, 13 of them in 70 seconds, and the PHP code cache was full and not configurable on that plan. The code was not failing; the platform was terminating it. The provider confirmed that the limits could not be changed.
That finding changed the decision. Without it, the next weeks would have gone into more code changes aimed at a problem the code did not have. With it, the question became whether to stay on a platform with a documented ceiling, and the answer was a dedicated server.
Before deciding, I measured how much of the slowness was the platform. A PHP profile showed the same code running 4 to 5 times slower on the shared hosting than locally. Two functions went from 58.5 to 0.5 ms with identical HTML output. A widely recommended optimisation, CSS aggregation, was tested and discarded because it did not improve the metrics. Knowing which tuning does not help is part of the result.
The migration was treated as a reliability project, not a copy: sixteen numbered, idempotent scripts rehearsed in containers and verified with checksums, the new server isolated until cutover, 1,815,094 database records checked one by one across 139 tables and 413,586 files, DNS rollback rehearsed, cutover with the shop closed and the old hosting intact, final proof with a real order.
Result at the origin, average of 10 pages: TTFB 954 to 636 ms on desktop (-33%) and 1,027 to 647 ms on mobile (-37%). Import on the new server: 188 records in 2 minutes 20 seconds, no process killed.
Why it matters
Reliability work often starts with a wrong attribution: the error is in the application, so the application gets patched. Here the error was in the layer below, and the only way to see it was to read logs from different layers together.
Limits
No load test was run on shared hosting during this phase: the contract required written authorisation and the engagement did not include it. Lab measurements against the origin, bypassing the CDN cache, not field data. The import figure is one observed run, not a statistical series.
Anonymised at the client’s request.