Profile Management
Browser profiles are the foundation of successful automation. A profile represents a complete browser identity that persists across sessions—cookies, fingerprints, local storage, and browsing history. Master profile management and you’ll master stealth automation.
What is a Browser Profile?
A browser profile is a persistent collection of:
| Component | What It Stores | Why It Matters |
|---|---|---|
| Fingerprint | Canvas, WebGL, audio, navigator properties | Consistent identity across visits |
| Cookies | Session tokens, preferences, tracking data | Maintains login state |
| Local Storage | App data, cached values | Site-specific persistence |
| Browser History | Visited URLs, timestamps | Behavioral authenticity |
| Cache | Images, scripts, stylesheets | Performance and realism |
When you use the same profile repeatedly, websites see a “returning user” rather than a suspicious new visitor.
Creating Profiles
Basic Profile Creation
The simplest way to create a profile:
import { GoLogin } from '@gologin/sdk';
const gologin = new GoLogin({ profileName: 'my-first-profile', createProfile: true, // Create if doesn't exist});
await gologin.start();// Profile is now created and runningawait gologin.stop();// Profile saved for future useProfile with Configuration
Create a profile with specific settings:
const gologin = new GoLogin({ profileName: 'us-east-profile', createProfile: true, platform: 'windows', locale: 'en-US', timezone: 'America/New_York', proxy: { type: 'http', host: 'us-east.proxy.com', port: 8080, },});Using the CLI
Create profiles from the command line:
# Create a basic profilegologin profile create --name "cli-profile"
# Create with optionsgologin profile create \ --name "advanced-profile" \ --platform windows \ --locale en-US \ --timezone "America/New_York"
# List all profilesgologin profile list
# Delete a profilegologin profile delete --name "old-profile"Profile Lifecycle
- Creation — Generate fingerprint, initialize storage directories
- First Use — Browser launches, fingerprint applied, clean state
- Session Activity — Cookies accumulate, localStorage populated
- Save — Session data persisted to disk on
gologin.stop() - Reuse — Same identity loaded, cookies restored, site sees returning user
- Aging — Profile accumulates history, becomes more trusted over time
Profile Aging Strategy
New profiles are suspicious. Aged profiles are trusted:
// Day 1: Create profile, light activityconst gologin = new GoLogin({ profileName: 'new-account' });await gologin.start();// Visit homepage, browse a few pagesawait gologin.stop();
// Day 2-7: Build browsing history// Regular visits, search activity, reading behavior
// Day 8+: Production use// Profile now has history and appears legitimateProfile Storage
Default Location
Profiles are stored in ~/.gologin/profiles/ with this structure:
~/.gologin/profiles/├── my-profile/│ ├── profile.json # Profile configuration│ ├── fingerprint.json # Browser fingerprint data│ ├── Default/ # Chromium user data│ │ ├── Cookies # Cookie database│ │ ├── Local Storage/ # localStorage data│ │ └── ...│ └── metadata.json # Profile metadata└── another-profile/ └── ...Custom Storage Location
Change the profiles directory:
const gologin = new GoLogin({ profileName: 'my-profile', profilesDir: '/path/to/profiles',});Or via environment variable:
export GOLOGIN_PROFILES_DIR=/data/browser-profilesMulti-Profile Management
One Profile Per Account
The golden rule: never share profiles between accounts.
// Good: Each account has its own profileconst accounts = ['user1@example.com', 'user2@example.com'];
for (const account of accounts) { const gologin = new GoLogin({ profileName: `twitter-${account.replace('@', '-at-')}`, });
await gologin.start(); // Login and operate this specific account await gologin.stop();}Profile Pools
Manage pools of profiles for different purposes:
interface ProfilePool { name: string; profiles: string[]; inUse: Set<string>;}
class ProfileManager { private pools: Map<string, ProfilePool> = new Map();
createPool(name: string, count: number) { const profiles = Array.from( { length: count }, (_, i) => `${name}-${i.toString().padStart(3, '0')}` ); this.pools.set(name, { name, profiles, inUse: new Set() }); }
acquire(poolName: string): string | null { const pool = this.pools.get(poolName); if (!pool) return null;
for (const profile of pool.profiles) { if (!pool.inUse.has(profile)) { pool.inUse.add(profile); return profile; } } return null; // All profiles in use }
release(poolName: string, profileName: string) { const pool = this.pools.get(poolName); if (pool) { pool.inUse.delete(profileName); } }}
// Usageconst manager = new ProfileManager();manager.createPool('scraping', 20);
const profile = manager.acquire('scraping');if (profile) { const gologin = new GoLogin({ profileName: profile }); await gologin.start(); // ... do work ... await gologin.stop(); manager.release('scraping', profile);}Concurrent Profiles
Run multiple profiles simultaneously:
import { GoLogin } from '@gologin/sdk';import puppeteer from 'puppeteer-core';
async function runProfile(profileName: string, task: (page: any) => Promise<void>) { const gologin = new GoLogin({ profileName }); const { browserWSEndpoint } = await gologin.start(); const browser = await puppeteer.connect({ browserWSEndpoint });
try { const page = await browser.newPage(); await task(page); } finally { await browser.close(); await gologin.stop(); }}
// Run 5 profiles concurrentlyconst profiles = ['profile-1', 'profile-2', 'profile-3', 'profile-4', 'profile-5'];
await Promise.all(profiles.map(profile => runProfile(profile, async (page) => { await page.goto('https://example.com'); // ... perform task ... })));Profile Rotation
When to Rotate
| Scenario | Rotation Frequency | Reason |
|---|---|---|
| Price monitoring | Daily | Avoid pattern detection |
| Social scraping | Per session | Fresh identity each time |
| Account management | Never | Maintain account identity |
| High-volume scraping | Per 100-500 requests | Distribute load |
Rotation Strategies
Round-Robin Rotation:
class ProfileRotator { private profiles: string[]; private currentIndex = 0;
constructor(profiles: string[]) { this.profiles = profiles; }
next(): string { const profile = this.profiles[this.currentIndex]; this.currentIndex = (this.currentIndex + 1) % this.profiles.length; return profile; }}
const rotator = new ProfileRotator(['p1', 'p2', 'p3', 'p4', 'p5']);
for (let i = 0; i < 100; i++) { const profile = rotator.next(); // Use profile...}Weighted Rotation (favor successful profiles):
class WeightedRotator { private profiles: Map<string, { weight: number; successes: number; failures: number }>;
constructor(profileNames: string[]) { this.profiles = new Map( profileNames.map(name => [name, { weight: 1.0, successes: 0, failures: 0 }]) ); }
next(): string { const entries = Array.from(this.profiles.entries()); const totalWeight = entries.reduce((sum, [, data]) => sum + data.weight, 0); let random = Math.random() * totalWeight;
for (const [name, data] of entries) { random -= data.weight; if (random <= 0) return name; } return entries[0][0]; }
recordSuccess(profileName: string) { const data = this.profiles.get(profileName); if (data) { data.successes++; data.weight = Math.min(2.0, data.weight * 1.1); // Increase weight } }
recordFailure(profileName: string) { const data = this.profiles.get(profileName); if (data) { data.failures++; data.weight = Math.max(0.1, data.weight * 0.5); // Decrease weight } }}Profile Backup and Migration
Export Profile
import { exportProfile } from '@gologin/sdk';
// Export to GoLogin formatawait exportProfile('my-profile', '/backups/my-profile.gologin');
// Export to ZIPawait exportProfile('my-profile', '/backups/my-profile.zip', { format: 'zip' });Import Profile
import { importProfile } from '@gologin/sdk';
// Import from GoLogin formatawait importProfile('/backups/my-profile.gologin', 'restored-profile');
// Import from MultiLogin formatawait importProfile('/migration/multilogin.mlx', 'from-multilogin', { format: 'multilogin'});Bulk Operations
# Export all profilesgologin profile export-all --output /backups/
# Import from directorygologin profile import-all --input /backups/Profile Health Monitoring
Track profile performance over time:
interface ProfileHealth { profileName: string; lastUsed: Date; totalSessions: number; successRate: number; lastError?: string; status: 'healthy' | 'degraded' | 'blocked';}
class ProfileHealthMonitor { private health: Map<string, ProfileHealth> = new Map();
recordSession(profileName: string, success: boolean, error?: string) { const current = this.health.get(profileName) || { profileName, lastUsed: new Date(), totalSessions: 0, successRate: 1.0, status: 'healthy', };
current.lastUsed = new Date(); current.totalSessions++;
// Update success rate with exponential moving average const alpha = 0.1; current.successRate = alpha * (success ? 1 : 0) + (1 - alpha) * current.successRate;
if (!success) { current.lastError = error; }
// Update status based on success rate if (current.successRate > 0.9) { current.status = 'healthy'; } else if (current.successRate > 0.5) { current.status = 'degraded'; } else { current.status = 'blocked'; }
this.health.set(profileName, current); }
getHealthy(): string[] { return Array.from(this.health.entries()) .filter(([, h]) => h.status === 'healthy') .map(([name]) => name); }
getBlocked(): string[] { return Array.from(this.health.entries()) .filter(([, h]) => h.status === 'blocked') .map(([name]) => name); }}Best Practices
1. Naming Conventions
Use clear, consistent naming:
// Good: Purpose + identifier'amazon-scraper-001''twitter-user1-at-example''pricing-monitor-us-east'
// Bad: Unclear or random'profile1''test123''abc'2. Regular Maintenance
Clean up old or blocked profiles:
async function cleanupProfiles(manager: ProfileHealthMonitor) { const blocked = manager.getBlocked();
for (const profile of blocked) { // Option 1: Delete and recreate await deleteProfile(profile); await createProfile(profile);
// Option 2: Clear cookies only await clearProfileCookies(profile); }}3. Separate by Purpose
Don’t mix use cases:
// Good: Dedicated profiles per purposeconst scrapingProfiles = ['scrape-01', 'scrape-02', 'scrape-03'];const accountProfiles = ['account-user1', 'account-user2'];
// Bad: Reusing profiles across different tasks4. Resource Limits
Set limits to prevent runaway profile creation:
const MAX_PROFILES = 100;
async function createProfileSafe(name: string) { const existingProfiles = await listProfiles();
if (existingProfiles.length >= MAX_PROFILES) { throw new Error(`Profile limit reached (${MAX_PROFILES})`); }
return createProfile(name);}Troubleshooting
Profile Won’t Load
// Check if profile existsimport { profileExists } from '@gologin/sdk';
if (!await profileExists('my-profile')) { console.log('Profile does not exist, creating...'); // Create profile}Corrupted Profile
If a profile becomes corrupted:
import { repairProfile, deleteProfile, createProfile } from '@gologin/sdk';
try { await repairProfile('corrupted-profile');} catch { // If repair fails, recreate await deleteProfile('corrupted-profile'); await createProfile('corrupted-profile');}Storage Space Issues
Monitor and clean profile storage:
# Check profile sizesdu -sh ~/.gologin/profiles/*
# Clean cache from all profilesgologin profile clean-cache --allNext Steps
- Configuration Options — Fine-tune profile settings
- Quick Start Guide — Launch your first session
- Multi-Account Management — Scale to many accounts