Skip to content

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:

ComponentWhat It StoresWhy It Matters
FingerprintCanvas, WebGL, audio, navigator propertiesConsistent identity across visits
CookiesSession tokens, preferences, tracking dataMaintains login state
Local StorageApp data, cached valuesSite-specific persistence
Browser HistoryVisited URLs, timestampsBehavioral authenticity
CacheImages, scripts, stylesheetsPerformance 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 running
await gologin.stop();
// Profile saved for future use

Profile 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:

Terminal window
# Create a basic profile
gologin profile create --name "cli-profile"
# Create with options
gologin profile create \
--name "advanced-profile" \
--platform windows \
--locale en-US \
--timezone "America/New_York"
# List all profiles
gologin profile list
# Delete a profile
gologin profile delete --name "old-profile"

Profile Lifecycle

  1. Creation — Generate fingerprint, initialize storage directories
  2. First Use — Browser launches, fingerprint applied, clean state
  3. Session Activity — Cookies accumulate, localStorage populated
  4. Save — Session data persisted to disk on gologin.stop()
  5. Reuse — Same identity loaded, cookies restored, site sees returning user
  6. 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 activity
const gologin = new GoLogin({ profileName: 'new-account' });
await gologin.start();
// Visit homepage, browse a few pages
await gologin.stop();
// Day 2-7: Build browsing history
// Regular visits, search activity, reading behavior
// Day 8+: Production use
// Profile now has history and appears legitimate

Profile 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:

Terminal window
export GOLOGIN_PROFILES_DIR=/data/browser-profiles

Multi-Profile Management

One Profile Per Account

The golden rule: never share profiles between accounts.

// Good: Each account has its own profile
const 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);
}
}
}
// Usage
const 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 concurrently
const 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

ScenarioRotation FrequencyReason
Price monitoringDailyAvoid pattern detection
Social scrapingPer sessionFresh identity each time
Account managementNeverMaintain account identity
High-volume scrapingPer 100-500 requestsDistribute 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 format
await exportProfile('my-profile', '/backups/my-profile.gologin');
// Export to ZIP
await exportProfile('my-profile', '/backups/my-profile.zip', { format: 'zip' });

Import Profile

import { importProfile } from '@gologin/sdk';
// Import from GoLogin format
await importProfile('/backups/my-profile.gologin', 'restored-profile');
// Import from MultiLogin format
await importProfile('/migration/multilogin.mlx', 'from-multilogin', {
format: 'multilogin'
});

Bulk Operations

Terminal window
# Export all profiles
gologin profile export-all --output /backups/
# Import from directory
gologin 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 purpose
const scrapingProfiles = ['scrape-01', 'scrape-02', 'scrape-03'];
const accountProfiles = ['account-user1', 'account-user2'];
// Bad: Reusing profiles across different tasks

4. 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 exists
import { 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:

Terminal window
# Check profile sizes
du -sh ~/.gologin/profiles/*
# Clean cache from all profiles
gologin profile clean-cache --all

Next Steps