All Server Guides Troubleshooting

Why seeking in Audiobookshelf jumps back to the start

Seeking a podcast or large single-file audiobook resets playback to the start in mobile Audiobookshelf apps. Why it happens and how to fix the file.

A glowing neon server tower on the left streaming an orange audio waveform across a dark circuit-board floor toward a smartphone on the right, with a bright pink cloud icon interrupting the waveform in the middle

You’re listening to a podcast or a single-file audiobook in a mobile Audiobookshelf app. You tap to skip ahead, and instead of moving forward, playback jumps straight back to 0:00. The web player in a desktop browser seeks fine. Multi-file audiobooks (a folder of small MP3s) seek fine. Only the big single files break, and it happens on every native iOS client people have tried (the official app, Plappa, ShelfPlayer) and on Android too.

This is almost never the server. On a direct-play session Audiobookshelf serves the file exactly as it’s stored on disk (res.sendFile in the session controller), which supports HTTP range requests, the byte-range reads a player uses to jump around inside a file. Both the maintainer and contributors on the tracking issue landed on the same conclusion: ABS just hands the raw file to your phone’s media player, so a seek that fails is failing either in the file itself or on the network path between the app and the server.

The reason it breaks on phones but not in the browser comes down to the player. A desktop browser’s audio element will estimate a seek position and tolerate some imprecision. The native players on phones (AVPlayer on iOS, ExoPlayer on Android) are stricter: they seek by asking the server for a specific byte offset, and to know which byte maps to “20 minutes in” they need the file’s seek table. Multi-file audiobooks sidestep the whole thing because each chapter is its own small file. A single 80 MB podcast episode is one long file, so the player has to seek inside it, and that’s where it falls over.

Confirm it’s the file

Most reports trace back to the file. The person who opened the issue fixed it by re-encoding the episode, and seeking worked right afterward, even re-encoding MP3 back to MP3. A contributor pinned it on “corrupted or incorrect headers,” which lines up: the usual culprit is a variable-bitrate (VBR) MP3 written without a proper header. With no header there’s no seek table, so the player can’t map a timestamp to a byte position and bails to the start.

Quick way to tell file from network: download the item for offline playback in your app, then try to seek on the local copy.

  • Still snaps back to 0 offline? It’s the file. Re-encode it.
  • Seeks fine offline but breaks while streaming? It’s the network path. Skip down to the Cloudflare section.

Re-encode the file

Start with the lossless option. This re-muxes the file without touching the audio, and the MP3 muxer writes a fresh header (and seek table) on the way out:

# No quality loss, just rewrites the container/header
ffmpeg -i broken.mp3 -c:a copy fixed.mp3

Test seeking on fixed.mp3. If it works, you’re done and you lost no quality.

If it still misbehaves, re-encode to constant bitrate (CBR). With CBR the player can work out the byte offset arithmetically without a seek table, so seeking is about as reliable as it gets:

# Constant bitrate, seeking works without a seek table
ffmpeg -i broken.mp3 -c:a libmp3lame -b:a 128k fixed.mp3

Drop the file back into your library folder and run a scan. For audiobooks you can also convert to a single M4B; the same re-encode advice and its gotchas are in the M4B goes silent post.

Podcasts are the annoying case

Audiobookshelf downloads podcast episodes itself, and it won’t re-encode them for you. There’s no “fix this episode” button in the UI. So either you re-encode episodes after they land, or you keep ABS out of the download loop and pre-process feeds yourself.

A periodic re-mux pass over the podcast folder fixes headers in place. Back up the folder first, this overwrites files:

# Re-mux every mp3 under the podcast folder to rewrite its header
find /path/to/podcasts -name '*.mp3' -exec sh -c '
  ffmpeg -nostdin -loglevel error -i "$1" -c:a copy "$1.tmp" && mv "$1.tmp" "$1"
' _ {} \;

Run it on a cron or an inotify watch so new downloads get cleaned up automatically.

If it only breaks over the internet

If seeking works on your local network and offline but resets to 0 when you’re streaming from outside the house, the file is fine and the problem is in the transport. Something between the app and the server isn’t passing range requests through, so when the player asks for “the bytes at 20 minutes,” it gets the whole file from the start and lands at 0:00.

The one report of this points at Cloudflare. A user running their server behind a Cloudflare Tunnel found seeking large files reset to the start until they turned off the orange-cloud proxy on the DNS record (set it to “DNS only”). They flagged it as a guess rather than a confirmed fix, and the issue was closed without any code change, so treat this as a lead, not a guaranteed solution.

If you’re on Cloudflare’s proxy and hitting this, worth trying:

  • Set the audio host’s DNS record to “DNS only” (grey cloud) instead of “Proxied” (orange cloud). The tradeoff is you lose Cloudflare’s proxy in front of your origin, so your server’s IP is exposed.
  • Or route audio through something that passes range requests cleanly, like a direct reverse proxy (nginx or Caddy) or Tailscale. The reverse proxy guide walks through a setup that works.

You can check whether range requests survive your setup. Grab a playing audio URL from the web player’s network tab (it includes the auth token), then:

# 206 = range works. 200 with the full file = something ate the range request
curl -s -D - -o /dev/null -r 1000000-1000100 "PASTE_AUDIO_URL_HERE"

A healthy response starts with HTTP/2 206 and includes a content-range: header. If you get 200 and the full content-length back, whatever sits in front of your server stripped the range request, and that’s your seeking bug.

On iPhone, use offline mode to find the cause

SoundLeaf streams through the same iOS AVPlayer the other apps use, so a streamed file with a bad header, or one behind a range-breaking proxy, will reset to the start in SoundLeaf too. There’s no app-side trick for it; the fix is the file or the network path.

What the app makes easy is the offline test from the top of this post. Download the episode, then seek on the local copy: if it seeks fine offline, it’s the network path; if it still snaps back, it’s the file.

Affected versions

This was reported on Audiobookshelf 2.30.0, and downgrading to 2.29.0 didn’t help, so it’s not a regression in a particular release. Audiobookshelf serves range requests correctly on current versions (checked through 2.34.0); it’s handing your player the file as-is. The fix lives in the file, or in whatever sits between your app and the server, not in a server upgrade.