Just another day of developer before holidays…
- Nothing Lasts Forever. Not Even 302 expires max
- It should be Nuxt middleware redirect
- Nuxt, Node 16, and the –openssl-legacy-provider Headache
- GitLab SSH key –
Permission denied (publickey)
fix - Bash gotcha: When your
.env
file disappears - WordPress cache issues
- WordPress CSS tr::after { content: “; } overflow
- Broken Padding with env()
1. Nothing Lasts Forever. Not Even expires max
.
So here’s what happened.
My colleague (whom I still respect, mostly) set up a temporary 302 redirect in Nginx. All good — except he also gave it a little gift:
location / {
return 302 /video;
expires max; # 😬
}
Yep. expires max
. On a 302.
The result? Browsers locked that redirect in like it was written in blood. Even after we removed the redirect on the server, users were still being thrown to /video
as if nothing had changed. Why? Because cache is a liar with a great memory.
The Fix?
First, remove that cursed line:
location / {
return 302 /video;
# expires max; ← 🔥 delete this with prejudice
}
Then, since users already had the redirect cached, I dropped this little JS bomb on /video
:
<script>
window.addEventListener('load', () => {
if (!localStorage.getItem('redirect-fix-done')) {
fetch('/', { cache: 'reload' })
.finally(() => localStorage.setItem('redirect-fix-done', '1'));
}
});
</script>
One silent fetch, and browsers finally got the memo. Redirect gone, cache busted, sanity restored.
Moral of the story?
Nothing lasts forever.
Not evenexpires max
— if you fight back.
2. It should be Nuxt middleware redirect
import { getRouteName, ROUTES } from '@/constants/routes';
const RELEASE_DATE = new Date('2025-05-01 00:00:00');
export default function middlewareVideo({ route, redirect, store }) {
if (new Date() > RELEASE_DATE) return;
const ALLOWED_ROUTES = new Set([
ROUTES.LOGIN,
ROUTES.PASSWORD.RESET,
ROUTES.PASSWORD.TOKEN,
]);
if (!ALLOWED_ROUTES.has(getRouteName(route)) && !store.getters['user/isLogged']) {
return redirect('/video');
}
}
and then on the /video
page we got a countdown set to same RELEASE_DATE
. On the end it just redirect to /
. Simple.
Just add that to nuxt.config.js
:
...
router: {
middleware: ['redirect-to-video'],
},
...
3. Nuxt, Node 16, and the –openssl-legacy-provider Headache
While working on a legacy Nuxt 2 project, I ran into a cryptic error:
/usr/local/n/versions/node/16.15.0/bin/npm run dev
node: --openssl-legacy-provider is not allowed in NODE_OPTIONS
The root cause? A mismatch between the Node and npm versions defined in package.json
and those actually used by the project. The fix was simple: I ran n auto
, which automatically set the correct Node and npm versions based on engines
. After that, the error disappeared when running from the terminal.
However, in PHPStorm, the error persisted. The solution there was to check the Run/Debug Configurations and remove the NODE_OPTIONS=--openssl-legacy-provider
environment variable, if present. Once that was cleaned up, everything worked as expected.
Small issue, quick fix — but worth documenting.
4. GitLab SSH key – Permission denied (publickey)
fix
When working with GitLab over SSH, I hit a Permission denied (publickey)
error even though everything seemed correctly set up. The actual issue? My old SSH key had expired, and GitLab no longer accepted it.
- Generate a new SSH key:
ssh-keygen -t ed25519 -C "super@superdeveloper.sk"
- Update
~/.ssh/config
to use the new key for GitLab:
Host gitlab.com
HostName gitlab.com
User git
IdentityFile ~/.ssh/id_ed25519_2025
After that, SSH auth worked again. Don’t forget to add the new public key (.pub
) to your GitLab account under Settings → SSH Keys.
5. Bash gotcha: When your .env
file disappears
While working on a simple start.sh
script to run a Docker container with a bound .env
file, I encountered a surprisingly tricky problem: the script needed to work regardless of where it was executed from — manually, from an IDE like PHPStorm, or via absolute/relative paths.
The goal was simple: always mount the .env
file located next to the script itself.
❌ What didn’t work
Here’s how I originally tried to bind the file:
#!/bin/bash
docker run --rm \
--mount type=bind,source="$(pwd)/.env",target=/usr/local/super/.env,readonly \
$IMAGE_NAME
This works fine if you run the script from the correct directory. But when executed from another location or via PHPStorm, $(pwd)
no longer points to where the script lives — and the mount fails silently.
✅ The fix: use the script’s own path
To make the script fully portable, I replaced $(pwd)
with:
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)"
This reliably resolves the absolute path to the directory where the script resides — even on macOS and when run from PHPStorm.
🧪 Final working version
#!/bin/bash
SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)"
ENV_FILE="$SCRIPT_PATH/.env"
if [[ ! -f "$ENV_FILE" ]]; then
echo "❌ .env file not found at $ENV_FILE"
exit 1
fi
echo "✅ Using .env from: $ENV_FILE"
docker run --rm \
--mount type=bind,source="$ENV_FILE",target=/usr/local/super/.env,readonly \
$IMAGE_NAME
🧠 Lesson learned
Don’t trust pwd
in your scripts. Always resolve paths relative to the script, not the shell. Your future self (and your IDE) will thank you.
6. WordPress cache issues
I don’t even want to talk about it. I have nothing against WordPress—it’s (kinda) a good CMS when used properly. But we’re dealing with a legacy e-shop using WooCommerce, WP Bakery plugin, and the Porto theme. Even something as simple as changing cart buttons is a pain (thanks to the previous owners and a mix of plugins). After I managed to deploy the update relatively quickly, nothing changed on the front end. Purging every cache we could find didn’t help. Eventually, renaming the CSS file worked (changing the enqueued style versions didn’t), but it also broke the homepage. So, more cache purging, more minifying plugins, and plenty of head-scratching followed.
7. WordPress CSS tr::after { content: “; } overflow
Already on my way out of the apartment when my colleague DM’d me: „Add to cart with gifts on mobile is broken.“ Of course, it worked perfectly on desktop. But on iPhone, there was this overlay covering the entire cart, making it impossible to click on the gift items. I connected my phone to the Mac and opened Safari’s Developer Tools. Turns out, the issue was with a tr::after { content: ""; }
that was meant to overlay a tr
with a disabled class. The approach seemed fine, but not all browsers were rendering it properly, even when the tr
was set to relative
and the overlay was absolute
with top/left: 0
and width/height: 100%
.
So, a quick fix: remove that content tr::after { content: none; }
and instead apply opacity: 0.5
to the tr.disabled
. Same result, but a simpler and more importantly, cross-browser working solution. I sent my colleague those two lines of code and ran away, leaving them to play the WordPress cache game.
8. Broken Padding with env()
Last problem of the day was when my junior colleague asked why this CSS rule wasn’t working on some older devices:
padding-left: max(56px, env(safe-area-inset-left));
It looked fine at first — but on certain devices (especially older Androids or embedded WebViews), the result was:
padding-left: 0px;
Why? Because some browsers don’t support env()
, and when they hit an unknown value inside max()
, they can ignore the entire rule.
📐 Understanding the Design Was the First Problem
She told me: „In the design, it’s exactly 56px from the left.“
Yes, but the design was done on a notch device, like an iPhone with a dynamic island. That meant the 56px
included the safe area.
When you hardcode 56px
, it feels too wide on non-notch devices.
When you rely on env()
, it breaks on devices that don’t support it.
Classic responsive trap.
✅ The Real Fix: Separate Layout and Safe-Area Padding
So we aligned with the designer:
Let’s use 20px layout padding, and if the device has a notch, just add the safe area on top of that.
On notch devices: 20px + safe-area
On others: just 20px
Here’s the pattern:
<div class="safe-area-wrapper">
<div class="content">
<!-- your content -->
</div>
</div>
.safe-area-wrapper {
padding-left: env(safe-area-inset-left, 0px);
padding-right: env(safe-area-inset-right, 0px);
}
.content {
padding-left: 20px;
padding-right: 20px;
}
This way, you always get at least 20px
padding — and if the device has a notch or curved edge, it adds that space without breaking the layout.