All posts

TikTok Kept Rejecting Our App. The Cause Was Our robots.txt.

7 min read
Illustration of a tiny robot in a lit doorway beneath an enormous wall sealed by a long red barrier

PassiveShorts publishes to TikTok on our users' behalf, so we need an approved TikTok for Developers app. We submitted for production review. It was rejected. We fixed what we thought was wrong, resubmitted, and it was rejected again. Same four fields every time:

  • App icon
  • Terms of Service URL
  • Website URL
  • Privacy Policy URL

The rejection message was the same templated paragraph each round. No specific field called out, no explanation of what failed, nothing a human had clearly written.

The cause turned out to be one line in a file we never wrote.

What we checked first, and why all of it was fine

We spent the early rounds assuming the reviewer was reading our pages and disliking them. So we went through the obvious list.

Domain verification. The tiktok-developers-site-verification TXT record was on the apex and resolving. Fine.

Page content. Our Terms and Privacy pages covered the disclosures TikTok's checklist asks for. Fine.

Reachability. Both URLs loaded in a browser, over HTTPS, with a 200 and no redirect chain. Fine.

Icon dimensions and format. Fine.

Everything a reviewer could plausibly object to was in order, and we kept getting the same generic paragraph back. That was the actual signal, and we misread it for several rounds.

A generic, identical rejection is usually an automated check failing, not a person disagreeing with you. A human reviewer who thinks your privacy policy is missing a data retention clause tends to say something about data retention. A validator that could not fetch the page has nothing specific to report, so you get the template.

Once we started treating it as a fetch failure rather than a content judgment, the question changed from "what is wrong with these pages" to "who is fetching these pages, and what do they see."

Who is fetching these pages

TikTok is owned by ByteDance. ByteDance's crawler is called Bytespider. Its user agent looks like this:

Mozilla/5.0 (Linux; Android 5.0) AppleWebKit/537.36 (KHTML, like Gecko)
Mobile Safari/537.36 (compatible; Bytespider; [email protected])

Bytespider is best known as an aggressive AI training crawler. It shows up in every "block AI bots" list published in the last two years, and it is on the blocklist that Cloudflare ships.

Our site is behind Cloudflare. We had turned on Cloudflare's managed robots.txt, which is the feature that writes AI crawler directives into your robots.txt for you. One toggle, sensible default, exactly the kind of thing you enable during setup and never think about again.

Then we actually read what it was serving at https://passiveshorts.com/robots.txt:

User-agent: Bytespider
Disallow: /

That is the whole bug. We had asked Cloudflare to keep AI scrapers away from our content, and Cloudflare had correctly done so, and one of the crawlers in that category was the one our app review depended on.

To the reviewer's validator, every URL we submitted was disallowed. Website URL, Terms of Service, Privacy Policy: all fetched by the same crawler, all blocked by the same line, all flagged. The app icon field is hosted on the same domain. Four fields, one root cause, zero error messages that mentioned any of it.

The part that surprised us

Bytespider has a well documented reputation for ignoring robots.txt. Search for it and you will find researchers logging it crawling disallowed paths, and guides telling you that a Disallow is useless against it and you need a WAF rule to actually stop it.

In our case, it obeyed.

We cannot see ByteDance's internals, so we cannot tell you whether app review runs on a different, better behaved fetcher than the training crawler, or whether something else in the Cloudflare AI bot configuration blocked it at the edge before robots.txt ever mattered. What we can tell you is what we changed and what happened:

We disabled Cloudflare's managed robots.txt, replaced it with an explicit allow-all file that we control in source, and resubmitted with no other changes. The app was approved on 25 June 2026.

The lesson is worth stating even though the mechanism is fuzzy: the "this crawler ignores robots.txt anyway" folklore is about the training crawler. It is not a safe assumption about every fetcher a company points at your domain, and it is a bad reason to skip checking your own robots.txt.

The fix

Our robots.txt now lives in the repo, at public/robots.txt, with a comment explaining why it must not be tightened:

# robots.txt for PassiveShorts
# Allow all crawlers full access to all content.
# Do not block Bytespider (ByteDance/TikTok) - it is needed for TikTok app review.
User-agent: *
Allow: /

Sitemap: https://passiveshorts.com/sitemap.xml

The comment matters more than the directives. Six months from now, somebody doing an AI scraping audit will look at this file, see that it welcomes every bot on the internet, and reach for the Cloudflare toggle. The comment is the only thing standing between that person and another month of mystery rejections.

If you want AI training crawlers blocked and your app approved, block them at the WAF by category and leave robots.txt permissive, or allow Bytespider explicitly above the blanket rule. Do not let a managed feature write that file for you while a vendor integration depends on its contents.

Two more things that cost us a round

Since we are describing this in detail, here are the other two things in the same review that were not the main cause but did burn a submission each.

The www subdomain was dead. www.passiveshorts.com returned a 522 while the apex was healthy. Every URL we hand to a vendor now points at the apex, with a trailing slash, because a reviewer following a link into a hostname you forgot you had is a rejection you will spend days failing to reproduce in your own browser.

The brand name has to be on the policy pages. TikTok checks that your Terms and Privacy pages visibly belong to the app being reviewed. "PassiveShorts" has to appear in the <h1> and in the <title> of /privacy/ and /tos/. This one has a trap in it: an SEO audit will flag identical h1 and title text as duplicate content, and the obvious fix is to drop the brand from one of them. Do not. Keep the brand in both and make the strings differ instead:

Page title h1
/privacy/ Privacy Policy | PassiveShorts PassiveShorts Privacy Policy
/tos/ Terms of Service | PassiveShorts PassiveShorts Terms of Service

Both checks pass. Nobody has to choose between the SEO tool and the app review.

A checklist for the next person

If a platform keeps rejecting your app with a generic message and no specific complaint, work the fetch path before you touch the content:

  1. Read your live robots.txt. Not the one in your repo. The one being served, including anything your CDN injects. If a managed feature can write to it, assume it has.
  2. Identify the vendor's crawler and check it against your blocklists. Bytespider is ByteDance, which is TikTok. The connection is not obvious from the user agent, and vendors do not warn you about it.
  3. Fetch your own URLs as that crawler. curl -A '<their user agent>' https://yoursite.com/privacy/ and confirm a 200 with real HTML, not a challenge page. Bot protection, rate limiting, and country blocking all fail here too, and all of them look fine from your browser.
  4. Check every hostname you might have submitted, including www, including trailing slash variants.
  5. Treat an identical, non-specific rejection as an automated failure. Rewriting your privacy policy a fourth time will not help if nothing is reading it.

That list would have saved us several weeks. Most of it is five minutes of curl.

Why we cared enough to write this up

PassiveShorts runs faceless video channels end to end: it writes the script, generates the video, and posts on a schedule you set. TikTok publishing is a large part of why people use it, and that requires an approved TikTok for Developers app. There is no version of this product where we shrug and skip the review.

The blocked review is also the reason we are comfortable saying our TikTok integration is a real one. Plenty of tools in this space generate a video and hand you a file to upload yourself, which conveniently avoids ever needing platform approval. We went and got approved, which is how we ended up with a month of our lives spent on one line of a text file.

If you want channels that post to TikTok and YouTube without you in the loop, that is what we built. The robots.txt is staying permissive.

Want this running without you?

PassiveShorts writes, voices, captions, and publishes faceless shorts to YouTube and TikTok on a schedule you set.

Write about tools like these? Earn 30% recurring commission on every customer you send us, for as long as they stay subscribed.