React Native国际象棋App安卓端WebSocket 403 Forbidden问题求助
I'm developing a chess app with React Native, using WebSocket to handle real-time communication with Lichess API. Everything works perfectly on iOS, but on Android, the WebSocket connection fails with the error: Expected HTTP 101 response but was '403 Forbidden'.
Here's my relevant code for creating a game and establishing the WebSocket connection:
createGame() { const { playConfig } = this.props; fetch('https://en.lichess.org/setup/ai', { method: 'POST', headers: { Accept: 'application/vnd.lichess.v2+json', 'Content-Type': 'application/json', }, body: playConfig, }) .then(res => res.json()) .then(this.onGameCreated); } onGameCreated = res => { const { game } = this.state; const socketUrl = res.url.socket; const clientId = Math.random().toString(36).substring(2); clearInterval(this.interval); this.wsReady = false; let url = `wss://socket.lichess.org${socketUrl}?sri=${clientId}&mobile=1`; this.ws = new WebSocket( url, ); this.ws.onmessage = e => { // a message was received console.log(`received: ${e.data}`); const data = JSON.parse(e.data); let moveData; let victor; if (data.t === 'move' && data.v > game.history().length) { moveData = data.d; } else if (data.t === 'end') { victor = data.d; } else if (data.t === 'b') { // b for batch const first = data.d[0]; if (first) { if (first.d.status && first.d.status.name === 'mate') { moveData = first.d; } if (first.t === 'end') { victor = first.d; } if (first.d.winner) { victor = first.d.winner; } } } if (victor) { dongSound.play(); this.setState({ victor, }); this.ws = null; } else if (moveData) { const { uci, clock } = moveData; const castle = moveData.castle; let from = uci.substring(0, 2); let to = uci.substring(2, 4); if (castle && castle.king) { from = castle.king[0]; to = castle.king[1]; } this.board.movePiece(to, from); if (clock) { this.latestClock = clock; } } }; this.ws.onerror = e => { // an error occurred console.log(e.message); }; this.ws.onopen = () => { this.wsReady = true; dongSound.play(); this.setState({ initialized: true, userColor: res.player.color === 'white' ? 'w' : 'b', }); console.log('ws open'); // ping every second this.interval = setInterval( () => { this.sendMessage({ t: 'p', v: game.history().length, }); }, 1000, ); }; };
Could any developer provide some troubleshooting ideas? Thanks in advance!
Looks like you're hitting a common cross-platform network discrepancy issue—iOS works fine, but Android's WebSocket gets rejected with a 403. This is almost always due to differences in request headers between the two platforms. Here are the most likely fixes, ordered by priority:
1. Force a Consistent User-Agent for WebSocket Requests
Lichess's server probably validates the User-Agent header, and Android's default WebSocket UA (usually something like okhttp/x.x.x) might be blocked, while iOS uses a Safari-like UA that's allowed.
How to Fix:
Since the native WebSocket constructor doesn't let you set custom headers directly, you have two options:
- Use a third-party WebSocket library that supports headers: For example,
react-native-websocket. Replace your native WebSocket initialization with this, specifying a User-Agent that matches iOS:import WebSocket from 'react-native-websocket'; // Inside onGameCreated this.ws = new WebSocket(url, [], { headers: { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1' } }); - Globally modify Android's network request UA: Edit your Android native code to add an OkHttp interceptor that sets a consistent User-Agent for all network requests (including WebSocket). In
MainApplication.java:import okhttp3.OkHttpClient; import okhttp3.Interceptor; import okhttp3.Request; import com.facebook.react.modules.network.OkHttpClientProvider; @Override public void onCreate() { super.onCreate(); OkHttpClient client = OkHttpClientProvider.getOkHttpClient(); client.interceptors().add(chain -> { Request original = chain.request(); Request modifiedRequest = original.newBuilder() .header("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1") .build(); return chain.proceed(modifiedRequest); }); }
2. Validate WebSocket URL Parameters
Even though iOS works, double-check that your URL parameters meet Lichess's requirements:
- Ensure the
sriparameter (yourclientId) has a valid length/format. Lichess might expect a specific length—try generating a longer string:const clientId = Math.random().toString(36).substring(2, 12); // 10-character random string - Verify that the
mobile=1parameter is correctly appended without encoding issues (your current code looks fine here, but it's worth confirming).
3. Check Android SSL Certificate Configuration
While 403 is a permission error, rare cases of strict SSL certificate validation on Android can cause unexpected issues:
- Confirm that Lichess's SSL certificate is trusted by Android (public CA certificates are usually trusted by default, so this is unlikely but worth checking).
- For testing only, temporarily disable SSL validation to rule out certificate issues (never do this in production).
4. Verify Android Network Permissions & Config
Make sure your AndroidManifest.xml includes the required internet permission:
<uses-permission android:name="android.permission.INTERNET" />
If your targetSdkVersion is 28 or higher, confirm that encrypted traffic (wss) is allowed (no need to enable cleartext traffic since you're using secure WebSocket).
Start with the User-Agent fix—it's the most common cause of this exact Android WebSocket 403 error with Lichess, and it should resolve your issue.
内容的提问来源于stack exchange,提问作者Mr.Siadati

